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: - с ними почти ничего не поделаешь, но любой, кто их использует, в любом случае знает, что нарушает контракт класса).

Другие вопросы по тегам