Изменчивый класс как ребенок неизменного класса - PullRequest
12 голосов
/ 22 марта 2010

Я хочу иметь неизменяемые объекты Java, подобные этим (сильно упрощенные):

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.

Что ты думаешь? Есть другие идеи?

Ответы [ 5 ]

14 голосов
/ 22 марта 2010

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

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

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

4 голосов
/ 23 января 2011

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

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

3 голосов
/ 22 марта 2010

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

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

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

2 голосов
/ 22 марта 2010

Ваш подкласс плох, потому что он нарушает принцип подстановки Лискова Не делай этого.

1 голос
/ 22 марта 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...