我有一个带有实例变量var的类。
我不希望将变量修改/分配给值,除非使用 Class 方法创建对象。
是不可变的:aBoolean 是将可变对象转换为不可变对象的方法,反之亦然。
有人可以给我提供正确的语法吗?
我还没有尝试过,但我很确定 isImmutable 不会解决问题。 假设这确实使对象不可变,它将使 instVar 指向的对象不可变,而不是 instvar 本身。
最好的办法是根本不包含变量的特定 Mutator,而是在初始化时设置它,如下所示:
MyClass class>>newWithFoo: aFoo
^self basicNew initializeWithFoo: aFoo; yourself
MyClass>>initializeWithFoo: aFoo
self initialize.
foo := aFoo.
这样,类本身之外的任何人都可以影响变量的唯一方法是通过调用MyClass newWithFoo:
创建一个新实例
(不包括使用像#instVarNamed:put:
这样的反射方法 - 你几乎无能为力,但任何使用它们的人都知道他们无论如何都会破坏类的契约)。
为什么要使对象不可变?以一种显而易见的方式声明您的 API 是否足以清楚地了解如何使用类而不替换实例变量?
我使用以下代码实现了它:
MyClass class>>classMethod: aValue
anObject := self new value:aValue.
anObject isImmutable: true.
^anObject.
要记住的一件事是,不可变性在对象图中的单个级别运行。实例变量有点像C++中的常量指针,地址不能改变,但内容可以改变。
这里有一个小例子:
| a b |
a := Array with: 1.
b := Array with: a.
b beImmutable.
b at: 1 put: nil.
^b
最终会得到一个NoModificationError,你不能写b的任何实例/索引变量,因为b是不可变的。
但是你可以写入 b 的实例/索引变量所指向的对象:
| a b |
a := Array with: 1.
b := Array with: a.
b beImmutable.
a at: 1 put: 2.
^b
将成功,b 现在是
#(#(2)) 而不是 #(#(1))你也可以安排在对象图上进一步传播不变性,如果这是你所追求的(但要注意循环)。