Smalltalk Как создать неизменяемую переменную экземпляра?
У меня есть класс с переменной экземпляра var.
Я не хочу, чтобы переменная изменялась / присваивалась значению, кроме случаев, когда объект создается с использованием метода Class.
isImmutable: aBoolean - это метод для преобразования изменяемого объекта в неизменяемый объект и наоборот.
Может кто-нибудь предоставить мне правильный синтаксис, чтобы сделать это?
4 ответа
Я реализовал это с помощью кода:
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 является неизменным.
Но вы можете записать в объекты, указанные переменными instance / indexed в b:
| a b |
a := Array with: 1.
b := Array with: a.
b beImmutable.
a at: 1 put: 2.
^b
Будет успешно, и b теперь #(#(2)) вместо #(#(1))
Вы также можете организовать распространение неизменности далее вниз по графу объектов, если когда-либо это то, что вы ищете (но остерегайтесь циклов).
Почему вы хотите сделать объект неизменным? Разве этого недостаточно, чтобы объявить ваш API таким образом, чтобы было понятно, как использовать класс, а не заменять переменную экземпляра?
Я не пробовал это, но я уверен, что неизменный не сработает. Предполагая, что это действительно делает объект неизменным, это сделает объект, на который указывает instVar неизменным, а не сам instvar.
Лучше всего вообще не включать в эту переменную определенный Mutator, а вместо этого установить его во время инициализации, например так:
MyClass class>>newWithFoo: aFoo
^self basicNew initializeWithFoo: aFoo; yourself
MyClass>>initializeWithFoo: aFoo
self initialize.
foo := aFoo.
Таким образом, единственный способ, которым кто-либо вне класса может повлиять на переменную, - это создать новый экземпляр, вызвав MyClass newWithFoo:
(Не считая, используя отражающие методы, такие как #instVarNamed:put:
- с ними почти ничего не поделаешь, но любой, кто их использует, в любом случае знает, что нарушает контракт класса).