Smalltalk Как создать неизменяемую переменную экземпляра?

У меня есть класс с переменной экземпляра var.
Я не хочу, чтобы переменная изменялась/присваивалась значение, за исключением случаев, когда объект создается с использованием метода класса.

isImmutable: aBoolean — это метод преобразования изменяемого объекта в неизменяемый и наоборот.
Может ли кто-нибудь предоставить мне правильный синтаксис для этого?


person Aditya Kappagantula    schedule 10.11.2013    source источник
comment
Я реализовал это, изменив метод доступа следующим образом: var1: anObject var1 := anObject. self isImmutable: true. Есть ли другой лучший способ сделать это?   -  person Aditya Kappagantula    schedule 10.11.2013


Ответы (4)


Я не пробовал, но я уверен, что isImmutable не поможет. Предполагая, что это действительно делает объект неизменяемым, он сделает неизменяемым объект, указанный instVar, а не сам instvar.

Лучше всего просто вообще не включать конкретный мутатор для переменной, а вместо этого установить его во время инициализации следующим образом:

MyClass class>>newWithFoo: aFoo
   ^self basicNew initializeWithFoo: aFoo; yourself

MyClass>>initializeWithFoo: aFoo
   self initialize.
   foo := aFoo.

Таким образом, единственный способ, которым кто-либо вне самого класса может повлиять на переменную, — это создать новый экземпляр, вызвав MyClass newWithFoo:

(Не считая использования рефлексивных методов, таких как #instVarNamed:put: - вы практически ничего не можете с ними поделать, но любой, кто их использует, знает, что они все равно нарушают контракт класса).

person Stuart Herring    schedule 10.11.2013
comment
Стюарт Херринг Я слышал, что модификация/реализация basicNew не является хорошей практикой объектно-ориентированного проектирования. Пожалуйста, поправьте меня, если я ошибаюсь. - person Aditya Kappagantula; 11.11.2013
comment
Я не предлагал вам модифицировать или реализовывать basicNew — только вызывайте его. То, что я предложил, было одним из двух распространенных: создать метод создания экземпляра на стороне класса — обычно вы либо вызываете #new, а затем общедоступные сеттеры для значений, которые хотите установить, либо вы вызываете #basicNew и явно вызываете метод инициализации, который установит начальные значения (что позволяет вам не иметь общедоступного установщика для этих переменных, что фактически дает вам неизменность). - person Stuart Herring; 12.11.2013

почему вы хотите сделать объект неизменным? Разве недостаточно объявить свой API таким образом, чтобы было очевидно, как использовать класс и не заменять переменную экземпляра?

person Karsten    schedule 11.11.2013
comment
Я пытался научиться использовать isImmutable в smalltalk. - person Aditya Kappagantula; 11.11.2013

Я реализовал это с помощью кода:

MyClass class>>classMethod: aValue

anObject := self new value:aValue.
anObject isImmutable: true.
^anObject.
person Aditya Kappagantula    schedule 11.11.2013

Следует иметь в виду, что неизменяемость работает на одном уровне графа объектов. Переменные экземпляра немного похожи на постоянный указатель в 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))

Вы также можете организовать распространение неизменности дальше по графу объектов, если это то, что вам нужно (но остерегайтесь циклов).

person aka.nice    schedule 13.11.2013