Почему бы мне не использовать неизменяемые объекты POJO вместо JavaBeans?


79

Я реализовал несколько приложений Java сейчас, пока только настольные приложения. Я предпочитаю использовать неизменяемые объекты для передачи данных в приложении вместо использования объектов с мутаторами (сеттерами и геттерами ), также называемыми JavaBeans.

Но в мире Java, кажется, гораздо более распространено использование JavaBeans, и я не могу понять, почему я должен использовать их вместо них. Лично код выглядит лучше, если он имеет дело только с неизменяемыми объектами, а не постоянно изменяет состояние.

Неизменяемые объекты также рекомендуются в Правиле 15: Минимизация изменчивости , Эффективная Java 2ed .

Если у меня есть объект, Personреализованный как JavaBean, он будет выглядеть так:

public class Person {
    private String name;
    private Place birthPlace;

    public Person() {}

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

    public setBirthPlace(Place birthPlace) {
        this.birthPlace = birthPlace;
    }

    public String getName() {
        return name;
    }

    public Place getBirthPlace() {
        return birthPlace;
    }
}

И то же самое, Personчто и неизменный объект:

public class Person {
    private final String name;
    private final Place birthPlace;

    public Person(String name, Place birthPlace) {
        this.name = name;
        this.birthPlace = birthPlace;
    }

    public String getName() {
        return name;
    }

    public Place getBirthPlace() {
        return birthPlace;
    }
}

Или ближе к structC:

public class Person {
    public final String name;
    public final Place birthPlace;

    public Person(String name, Place birthPlace) {
        this.name = name;
        this.birthPlace = birthPlace;
    }
}

У меня также могут быть геттеры в неизменяемом объекте, чтобы скрыть детали реализации. Но поскольку я использую его только как элемент, structя предпочитаю пропустить "геттеры" и сохранить простоту.

Просто я не понимаю, почему лучше использовать JavaBeans, или могу и должен продолжать использовать свои неизменяемые POJO?

Многие библиотеки Java, похоже, лучше поддерживают JavaBeans, но, может быть, со временем станет более популярной поддержка неизменных POJO?


1
Я думаю, вы имеете в виду пункт 15: минимизируйте изменчивость , а не * минимизируйте неизменяемость »
Мэтт Б.

@matt: Спасибо =) Я обновил его сейчас.
Jonas

7
Ваш подход к неизменяемости великолепен, он избавляет вас от множества проблем с потоками. +1
вискиьерра

1
При использовании JPA вам понадобится конструктор по умолчанию
Нил МакГиган

Ответы:


78

Предпочитать JavaBeans, когда

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

Предпочитайте неизменяемые POJO, когда

  • у вас есть небольшое количество простых свойств
  • вам не нужно взаимодействовать со средами, принимая соглашения JavaBean
  • легко (или, по крайней мере, возможно) скопировать состояние при клонировании вашего объекта
  • вы вообще не планируете клонировать объект
  • вы почти уверены, что вам никогда не придется изменять способ доступа к свойствам, как указано выше
  • вы не против слушать нытье (или насмешки) о том, что ваш код недостаточно "объектно-ориентированный"

+1 за листинг и почти -1 за ваше имя :) В конце концов, все знают, что это мое мнение, которое правильно. Конечно, каждый определяется в смысле Плоского мира.
extraneon

12
Проверка границ и проверка ввода могут быть выполнены в конструкторе. Если ввод недействителен или выходит за рамки, я не хочу создавать объект.
Jonas

24
Ваш второй аргумент в пользу beans не в пользу beans, а в пользу паттерна Builder. Вы все еще можете создать неизменяемый объект.

4
Вы не упоминаете потоки с неизменяемыми POJO, что, безусловно, является одной из главных причин их использования. Еще один важный момент - простота: когда объект не меняется, с ним легче работать в долгосрочной перспективе. Кроме того, с неизменяемыми объектами POJO вам не нужно беспокоиться о клонировании объекта в целом, вы можете просто передать его.
Deterb

Как вы строите графы объектов, которые могут иметь циклические ссылки, чтобы эти объекты были неизменными? Это возможно? Например, я хочу, чтобы классы A и B были неизменными, но A нужна ссылка на B, а B нужна ссылка на A. Есть ли способ сделать это?
chakrit 02 авг.13,

39

Я был удивлен, что слово Thread не появилось нигде в этом обсуждении.

Одним из основных преимуществ неизменяемых классов является то, что они по своей сути более потокобезопасны из-за отсутствия изменяемого общего состояния.

Это не только упростит программирование, но и даст два побочных эффекта в производительности:

  • Меньше необходимости в синхронизации.

  • Больше возможностей для использования конечных переменных, которые могут облегчить последующую оптимизацию компилятора.

Я действительно пытаюсь перейти к неизменяемым объектам, а не к классам стиля JavaBean. По-видимому, не следует использовать раскрытие сущности объектов с помощью геттеров и сеттеров.


18

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

Учтите, что у вашего человека много полей, таких как имя, отчество, фамилия, дата рождения, члены семьи, образование, работа, зарплата и т. Д.

И этот Человек оказался женщиной, которая только что вышла замуж и согласилась на изменение своей фамилии, и вам необходимо обновить базу данных.

Если вы используете неизменяемый POJO, вы выбираете объект Person, представляющий ее, затем вы создаете новый объект Person, которому вы передаете все свойства, которые вы не изменили, как они есть, и новую фамилию, и сохраняете ее.

Если бы это был компонент Java, вы можете просто выполнить setLastName () и сохранить его.

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


1
Но ваш пример почти никогда не бывает тем, что нужно делать в «реальных» приложениях. Обычно ваш обновленный объект данных необходимо передать в среду проверки (Hibernate Validator, Spring Validator и т.д.), где объект подвергается различным операциям, возможно, написанным разными разработчиками. Если объект является изменяемым, вы не знаете, работаете ли вы с теми же данными, которые были переданы (если некоторая проверка, выполненная до вашей, изменила объект на месте). Это делает процесс проверки менее детерминированным, более зависимым от строгого порядка и непараллелизируемым.
dsmith

Вдобавок «реальное» приложение, вероятно, будет иметь какой-то внутренний или распределенный кеш (EHCache и т. Д.), Содержащий объекты вашего домена. Если вы знаете, что ваши объекты неизменяемы, вам не нужно нести накладные расходы на копирование при чтении. Вы можете безопасно извлекать и использовать объект в памяти, не беспокоясь о целостности кеша. IMHO, для объектов бизнес-домена такого типа всегда нужны неизменяемые объекты. Изменяемые объекты подходят для локальной области видимости и некоторых других специализированных случаев, в которые у меня нет персонажей, чтобы разобраться здесь.
dsmith

7

Обобщая другие ответы, я думаю, что:

  • Неизменяемость способствует правильности (структуры могут передаваться по ссылке, и вы знаете, что ничего не будет уничтожено неисправным / вредоносным клиентом) и простоте кода
  • Изменяемость способствует однородности : Spring и другие фреймворки создают объект без аргументов, задают свойства объекта и voi là . Также упростите интерфейсы, используя один и тот же класс для передачи данных и сохранения изменений (вам не нужно get(id): Clientи save(MutableClient), поскольку MutableClient является потомком Client.

Если бы была промежуточная точка (создание, установка свойств, создание неизменяемости), возможно, фреймворки поощряли бы более неизменный подход.

В любом случае я предлагаю думать о неизменяемых объектах как о «Java Beans только для чтения», подчеркивая, что если вы хороший мальчик и не трогаете опасный метод setProperty, все будет в порядке.


Я согласен с тем, что лучшее из обоих миров было бы для библиотек и фреймворков, чтобы перейти к неизменному подходу. Преимущества неизменяемого являются внутренними, а преимущества изменяемого - случайными.
dsmith

3

Начиная с Java 7, вы можете иметь неизменяемые бины, лучшее из обоих миров. Используйте аннотацию @ConstructorProperties в своем конструкторе.

public class Person {
    private final String name;
    private final Place birthPlace;

    @ConstructorProperties({"name", "birthPlace"})
    public Person(String name, Place birthPlace) {
        this.name = name;
        this.birthPlace = birthPlace;
    }

    public String getName() {
        return name;
    }

    public Place getBirthPlace() {
        return birthPlace;
    }
}


1

Честно говоря, я не думаю, что неизменные объекты станут настолько популярными.

Я действительно вижу преимущества, но такие фреймворки, как Hibernate и Spring, сейчас очень популярны (и на то есть веская причина), и они действительно лучше всего работают с beans.

Поэтому я не думаю, что неизменяемость - это плохо, но она определенно ограничит ваши возможности интеграции с текущими фреймворками.

ИЗМЕНИТЬ Комментарии побуждают меня немного уточнить свой ответ.

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

И хотя в Spring действительно можно использовать конструкторы с аргументами, похоже, что это предназначено как способ использовать устаревший и / или сторонний код с красивым совершенно новым кодом Spring. По крайней мере, это то, что я взял из документации.


2
Идея IoC / DI основана на установке / вводе состояния, поэтому неизменяемость не так популярна в Spring. Однако Joda-Time - хороший пример правильной неизменяемости.
луноходов

Вы также можете использовать аргументы конструктора для внедрения зависимостей с помощью spring, просто кажется, что не многие люди делают это (возможно, потому, что, когда у вас много зависимостей, иметь конструктор параметров с 10+ страшно).
Андрей Фирбинтяну

6
@lunohodov: Я использовал Google Guice для DI / IoC, и нет проблем с внедрением зависимостей в конструктор.
Jonas

В общем, проще всего работать с вещами (особенно в многопоточной среде), если данные хранятся с использованием неизменяемых однажды созданных ссылок на изменяемые объекты или изменяемых ссылок на неизменяемые объекты. Наличие свободно изменяемых ссылок на изменяемые объекты усложняет задачу. Обратите внимание, что если Xи Yотносятся к одному и тому же объекту, и кто-то хочет изменить X.something, можно сохранить идентичность X и изменить Y.somethingили оставить Y.somethingнеизменным и изменить идентичность X, но нельзя сохранить идентичность X и состояние Y при изменении состояние X.
supercat 06

Те, кто настаивает на использовании неизменяемых объектов, иногда не осознают важность идентичности объекта и того факта, что изменяемые объекты могут иметь неизменяемую идентичность в отличие от неизменяемых объектов.
supercat 06

0

Неизменяемый с точки зрения программирования на Java: что-то, что однажды было создано, никогда не должно иметь изменения состояния, как ожидаемого, так и неожиданного!

Этот метод полезен в защитном программировании, когда другой объект не может вызвать изменение состояния.

Примеры, в которых вы не ожидаете изменений: внешние системы (однопоточные или многопоточные), которые получают ссылку с вашего уровня, оперируют объектом и сознательно или неосознанно изменяют его. Теперь это может быть объект POJO, коллекция или ссылка на объект, и вы не ожидаете в нем изменений или хотите защитить данные. Вы обязательно сделаете объект неизменным как защитную технику

Примеры, в которых вы ожидаете изменений: На самом деле неизменяемость не нужна, так как она будет мешать правильным процедурам программирования.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.