#Private attribute example
class C {
has $!w; #private attribute
multi method w { $!w } #getter method
multi method w ( $_ ) { #setter method
warn “Don’t go changing my w!”; #some side action
$!w = $_
}
}
my $c = C.new
$c.w( 42 )
say $c.w #prints 42
$c.w: 43
say $c.w #prints 43
#but not
$c.w = 44
Cannot modify an immutable Int (43)
Run Code Online (Sandbox Code Playgroud)
#Public attribute example
class C {
has $.v is rw #public attribute with automatic accessors
}
my $c = C.new
$c.v = 42
say $c.v #prints 42
#but not
$c.v( 43 ) #or $c.v: 43
Too many positionals passed; expected 1 argument but got 2
Run Code Online (Sandbox Code Playgroud)
我喜欢 '=' 赋值的即时性,但我需要多种方法提供的辅助操作的简易性。我明白这是两个不同的世界,它们不会混合。
但是 - 我不明白为什么我不能去 $cv( 43 ) 设置一个公共属性
Jon*_*ton 14
这是Raku设计的意图吗?
可以公平地说,Raku 在这方面并非完全没有意见。您的问题涉及 Raku 设计中的两个主题,这两个主题都值得讨论。
Raku 充分利用了 l 值作为一流的东西。当我们写:
has $.x is rw;
Run Code Online (Sandbox Code Playgroud)
生成的方法是:
method x() is rw { $!x }
Run Code Online (Sandbox Code Playgroud)
在is rw这里表明,该方法返回一个L值-也就是一些可以分配给。因此,当我们写:
$obj.x = 42;
Run Code Online (Sandbox Code Playgroud)
这不是语法糖:它确实是一个方法调用,然后将赋值运算符应用于它的结果。这是可行的,因为方法调用返回Scalar属性的容器,然后可以将其分配到。可以使用绑定将其分为两步,看看这不是一个简单的句法转换。例如,这个:
my $target := $obj.x;
$target = 42;
Run Code Online (Sandbox Code Playgroud)
将分配给对象属性。这种相同的机制是许多其他功能的背后,包括列表分配。例如,这个:
($x, $y) = "foo", "bar";
Run Code Online (Sandbox Code Playgroud)
通过构造List包含容器$xand 的$y,然后赋值运算符在这种情况下成对迭代每一边以进行赋值。这意味着我们可以rw在那里使用对象访问器:
($obj.x, $obj.y) = "foo", "bar";
Run Code Online (Sandbox Code Playgroud)
这一切都是自然而然的。这也是分配给数组和散列切片背后的机制。
还可以使用Proxy来创建一个 l 值容器,其中读取和写入它的行为在您的控制之下。因此,您可以将副作用放入STORE. 然而...
当我们描述 OO 时,经常会出现诸如“封装”和“数据隐藏”之类的术语。这里的关键思想是对象内部的状态模型 - 即它选择表示实现其行为(方法)所需数据的方式 - 可以自由发展,例如处理新需求。对象越复杂,它就越自由。
但是,getter 和 setter 是与状态有隐式联系的方法。虽然我们可能声称我们正在实现数据隐藏,因为我们正在调用一个方法,而不是直接访问状态,但我的经验是,我们很快就会在一个地方结束,外部代码正在执行一系列 setter 调用以实现操作——这是特征羡慕反模式的一种形式。如果我们正在做的是,它是相当肯定,我们将与不getter和setter混合操作来实现操作的对象的逻辑之外结束。实际上,这些操作应该公开为具有描述所实现内容的名称的方法。如果我们处于并发环境中,这将变得更加重要;设计良好的对象通常很容易在方法边界处得到保护。
也就是说,许多用途class实际上是记录/产品类型:它们的存在只是为了将一堆数据项组合在一起。这不是偶然的.印记不只是产生一个访问,也:
class Point { has $.x; has $.y; }可以实例化为Point.new(x => 1, y => 2)),并在.raku转储方法中呈现该属性。.Capture对象,这意味着我们可以在解构中使用它(例如sub translated(Point (:$x, :$y)) { ... })。如果您以更程序化或功能性的风格编写并class用作定义记录类型的手段,那么您会想要哪些东西。
Raku 设计并没有针对在 setter 中做聪明的事情进行优化,因为这被认为是一个不好优化的事情。它超出了记录类型的需要;在某些语言中,我们可能会争辩说我们想要对分配的内容进行验证,但在 Raku 中,我们可以subset为此转向类型。同时,如果我们真的在做 OO 设计,那么我们想要一个隐藏状态模型的有意义行为的 API,而不是从 getter/setter 的角度来思考,这往往会导致无法并置数据和行为,这无论如何都是 OO 的重点。
但是 - 我不明白为什么我不能去 $cv( 43 ) 设置一个公共属性
嗯,这真的取决于建筑师。但说真的,不,这根本不是 Raku 工作的标准方式。
现在,这将是完全有可能创造一个Attribute在模块的空间,类似的性状is settable,这将创建一个替代存取方法将接受一个单一的值来设置该值。在核心中这样做的问题是,我认为世界上基本上有两个阵营关于这样一个mutator的返回值:它会返回新值还是旧值?
如果您有兴趣在模块空间中实现这样的特性,请与我联系。
我目前怀疑你只是糊涂了。1在我谈到这个之前,让我们从你没有混淆的东西开始:
我喜欢
=任务的即时性,但我需要多种方法提供的辅助操作的简易性。...我不明白为什么我不能去$c.v( 43 )设置一个公共属性
你可以做所有这些事情。也就是说,您可以同时使用=赋值、多种方法和“just go $c.v( 43 )”,如果您想:
class C {
has $!v;
multi method v is rw { $!v }
multi method v ( :$trace! ) is rw { say 'trace'; $!v }
multi method v ( $new-value ) { say 'new-value'; $!v = $new-value }
}
my $c = C.new;
$c.v = 41;
say $c.v; # 41
$c.v(:trace) = 42; # trace
say $c.v; # 42
$c.v(43); # new-value
say $c.v; # 43
Run Code Online (Sandbox Code Playgroud)
在幕后,按照以下方式has $.foo is rw生成一个属性和一个方法:
has $!foo;
method foo () is rw { $!foo }
Run Code Online (Sandbox Code Playgroud)
不过上面说的不太对。鉴于我们看到的行为,编译器的自动生成foo方法以某种方式被声明为任何同名的新方法都会默默地隐藏它。2
因此,如果您想要一个或多个与属性同名的自定义方法,如果您希望保留它通常负责的行为,则必须手动复制自动生成的方法。
1请参阅 jnthn 的回答,了解 Raku 关于私有与公共 getter/setter 的观点的清晰、彻底、权威的说明,以及当您声明公共 getter/setter(即 write has $.foo)时它在幕后所做的事情。
2如果声明了一个属性的自动生成的访问器方法only,那么我认为,如果声明了一个具有相同名称的方法,Raku 会抛出异常。如果已声明multi,则在新方法也已声明multi的情况下不应隐藏它,否则应抛出异常。因此,自动生成的访问器被声明为既不是only也不是multi,而是以某种允许静默阴影的方式。
| 归档时间: |
|
| 查看次数: |
272 次 |
| 最近记录: |