为 public 属性使用自动生成的方法与创建访问私有属性的方法
Using automatically generated methods for public attributes vs creating methods for accessing private attributes
假设我有一个 class 有几个属性,但我只需要其中的一些来构造对象;其余属性的值取决于这些 public 属性。但是,我仍然想使用以属性命名的方法访问其余属性的值。在我的脑海中,有两个选择:
第一个选项:
- 重载
new
方法。或者保持 new
方法不变,而是使用 BUILD
子方法将传递的参数设置为正确的属性,并使用该参数的值设置其余属性。
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 属性,因此如果您尝试覆盖 b
或 c
这将被忽略。
如果你愿意,你可以添加一个 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
特征。
假设我有一个 class 有几个属性,但我只需要其中的一些来构造对象;其余属性的值取决于这些 public 属性。但是,我仍然想使用以属性命名的方法访问其余属性的值。在我的脑海中,有两个选择:
第一个选项:
- 重载
new
方法。或者保持new
方法不变,而是使用BUILD
子方法将传递的参数设置为正确的属性,并使用该参数的值设置其余属性。
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 属性,因此如果您尝试覆盖 b
或 c
这将被忽略。
如果你愿意,你可以添加一个 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
特征。