2010-03-22 5 views
12

Я хочу иметь неизменное Java объектов, как это (сильно упрощено):Mutable класса как ребенок неизменного класса

class Immutable { 

    protected String name; 

    public Immutable(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return name; 
    } 

} 

В некоторых случаях объект должен быть не только для чтения, но изменяемым, так что я мог бы добавить изменчивость по наследству:

public class Mutable extends Immutable { 

    public Mutable(String name) { 
     super(name); 
    } 

    public void setName(String name) { 
     super.name = name; 
    } 

} 

Хотя это технически хорошо, мне интересно, если это согласуется с объектно-ориентированным программированием и наследованием, что изменяемое также типа неизменных. Я хочу избежать преступления ООП, чтобы бросить UnsupportedOperationException для неизменяемого объекта, как это делает API коллекций Java.

Что вы думаете? Любые другие идеи?

+1

Это именно то, что делает Objective C: 'NSMutableString' наследует от' NSString', 'NSMutableArray' наследует от' NSArray' и т. Д. – Nefrubyr

ответ

14

Избегайте вызова родительского «Неизменяемого», потому что он становится ложью в дочернем классе - если вы хотите сделать класс неизменным, он также должен быть окончательным, чтобы предотвратить эту проблему.

Joda Time использует «ReadableXXX», чтобы дать понять, что «этот класс предоставляет доступ только для чтения, другие подклассы могут быть изменчивыми». Я не уверен, нравится ли мне эта идея, но я не могу сказать, что видел много альтернатив.

В основном проблема с выражения отрицательной - Immutable описывает то, что вы не можете сделать (мутировать его), и что не может быть разумно применяться в подклассах. (Даже если поля в пределах Immutable были окончательными, это не остановило бы подкласс с собственными изменчивыми полями.)

3

Неизменяемые классы должны быть final точно, чтобы избежать измененных подтипов.

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

Вот почему строка является окончательной.

+1

Считайте, что вам нужны базовые классы ImmutableCollection и подклассы ImmutableList, ImmutableSet. Как вы можете сделать здесь базовый класс? Дело в том, что ни один компилятор не может выразить все несоответствия, которые могут появиться в вашей модели. Таким образом, мы должны ожидать, что компилятор проверит все для нас. – Alexey

1

Я нахожу ваш код довольно любопытным.

Чтобы реализовать такое неизменное поведение, я предпочел бы использовать интерфейс Immutable, предоставляя только метод геттера, в то время как объект содержит оба. Таким образом, операции, основанные на неизменяемых объектах, вызвали бы интерфейс, в то время как другие называли бы объект.

И если вы действительно не хотите, чтобы ваши неизменяемые объекты были выбраны как изменчивые, вы можете использовать прокси-серверы и все инструменты предприятия (аспекты и т. Д.). Но обычно, полагаясь на добрую волю других разработчиков, это сладкий способ заставить их отвечать за свои ошибки (например, бросать неизменяемость в изменчивый).

+1

Прокси могут быть поданы в суд, чтобы сделать объект доступным только для чтения, но они не делают объект неизменным. Единственный способ сделать возможный изменяемый объект неизменным - отделить каждую его часть, которая может быть изменчивой, и в этом случае объект больше не является прокси-сервером. – supercat

2

Ваш подкласс плох, потому что он нарушает Liskov substitution principle. Не делай этого.

+2

Нет, нет. Экземпляры 'Immutable' могут быть заменены экземплярами' Mutable', не изменяя поведения кода. – emlai

+0

Я не понимаю, почему этот неправильный ответ все еще здесь, без каких-либо изменений. – uetoyo

+0

код, требующий неизменяемого объекта, должен быть обновлен для создания защитных копий. иначе в некоторых ситуациях могут произойти плохие вещи. возможно, это не технически LSP. – les2

4

Я бы предположил, что у вас должен быть наследуемый базовый класс «ReadableFoo», производный закрытый класс ImmutableFoo и другие производные классы MutableFoo. Код, который не заботится о том, изменен ли Foo или нет, может принять ReadableFoo. Код, который хочет Foo, который гарантированно не изменится, может принять ImmutableFoo. Код, который может понадобиться для изменения Foo, может принять MutableFoo.

Обратите внимание, что конструкторы как для ImmutableFoo, так и для MutableFoo обычно должны принимать ReadableFoo. Таким образом, любой Foo будет конвертируемым в изменяемую или неизменяемую версию.