为 public 属性使用自动生成的方法与创建访问私有属性的方法

Using automatically generated methods for public attributes vs creating methods for accessing private attributes

假设我有一个 class 有几个属性,但我只需要其中的一些来构造对象;其余属性的值取决于这些 public 属性。但是,我仍然想使用以属性命名的方法访问其余属性的值。在我的脑海中,有两个选择:

第一个选项:

class Foo {
    has Int $.a;
    has Int $.b;
    has Int $.c;

    submethod BUILD(:$!a) {
        $!b = $!a ** 2;
        $!c = $!a ** 3;
    }
}
say Foo.new(:2a, :1b);   #=> Foo.new(a => 2, b => 4, c => 8)
say Foo.new(:2a, :1b).b; #=> 8

第二个选项:

仅使用 $. 声明对象构造所需的那些属性,并在对象构造后使用 TWEAK 子方法修改其余属性(使用 $! 声明)。但是,现在我需要为这些属性创建访问器方法。

class Bar {
    has Int $.a;
    has Int $!b;
    has Int $!c;

    submethod TWEAK {
        $!b = $!a ** 2;
        $!c = $!a ** 3;
    }

    method b { $!b }
    method c { $!c }
}
say Bar.new(:2a, :1b);   #=> Bar.new(a => 2)
say Bar.new(:2a, :1b).b; #=> 8

问题

最佳选项是什么?还有其他选择吗?每个选项的 advantages/drawbacks 是多少?

几周前我读了一篇文章,其中谈到了 class 的构造函数参数与其 public 接口之间的这种歧义,但我再也找不到了。

但我认为您可以利用 FALLBACK

class Bar {
    has Int $.a;
    has Int $!b;
    has Int $!c;

    submethod TWEAK {
        $!b = $!a ** 2;
        $!c = $!a ** 3;
    }

    method FALLBACK( $name ) {
        self.^attributes.first( *.name eq "$!$name" ).?get_value(self);
    }
}

say Bar.new(:2a);
say Bar.new(:2a).c;

不过这有点棘手,而且成本很高,因为属性查找必须经过 FALLBACK 和自省。最好有一个可以创建访问器但对构造函数没有影响的特征。像

class Bar {
    has Int $.a;
    has Int $.b is shady; # or whatever name
    has Int $.c is shady;
}

但是我不知道有这样的事情存在。

就我个人而言,我会选择简单明了的方式:

class Foo { 
    has Int $.a; 
    has Int $!b = $!a ** 2; 
    has Int $!c = $!a ** 3;
    method b { $!b }
    method c { $!c }
}
say Foo.new(:2a, :1b); #=>  Foo.new(a => 2)
say Foo.new(:2a, :1b).b; #=> 4

只需使用属性的默认构造函数和默认值,并添加几个基本的读取方法。默认构造函数仅更新 public 属性,因此如果您尝试覆盖 bc 这将被忽略。

如果你愿意,你可以添加一个 BUILD 子方法来错误,如果有人尝试并设置它们。

评论中针对此案例(也可能是最终用例)提出的一个很好的观点是这样做的:

class Foo { 
    has Int $.a; 
    method b() is pure { $!a ** 2 }
    method c() is pure { $!a ** 3 }
}
say Foo.new(:2a, :1b); #=>  Foo.new(a => 2)
say Foo.new(:2a, :1b).b; #=> 4

根据计算的复杂性,您可能还想使用 is cached 特征。