Чому я не повинен використовувати незмінні 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;
    }
}

Я міг би також мати геттери в незмінному об’єкті, щоб приховати деталі реалізації. Але оскільки я використовую його лише як a, structя віддаю перевагу пропускати "геттери", і залишати його простим.

Просто я не розумію, чому краще використовувати JavaBeans, або якщо я можу і повинен продовжувати працювати зі своїми незмінними POJO?

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


1
Думаю, ви маєте на увазі пункт 15: Мінімізуйте мінливість , а не * Мінімізуйте незмінність "
matt b

@matt: Дякую =) Я оновив його зараз.
Йонас,

7
Ваш підхід до незмінності чудовий, економить багато проблем із потоками. +1
whiskeysierra

1
Вам потрібен конструктор за замовчуванням, якщо ви використовуєте JPA
Neil McGuigan

Відповіді:


78

Віддайте перевагу JavaBeans Коли

  • вам доведеться взаємодіяти із середовищем, яке їх очікує
  • у вас є багато властивостей, для яких було б незручно робити всю ініціалізацію під час створення
  • у вас є штат, який дорого або неможливо скопіювати з якихось причин, але вимагає мутації
  • ви думаєте, що в якийсь момент вам, можливо, доведеться змінити спосіб доступу до властивостей (наприклад, перехід від збережених до обчислених значень, авторизація доступу тощо)
  • ви хочете відповідати стандартам кодування, які бездумно наполягають, що якось більш «об’єктно-орієнтовано» використовувати JavaBeans

Віддайте перевагу незмінним POJO, коли

  • у вас невелика кількість простих властивостей
  • вам не доведеться взаємодіяти із середовищами, передбачаючи конвенції JavaBean
  • легко (або принаймні можливо) скопіювати стан при клонуванні об’єкта
  • ви взагалі ніколи не плануєте клонувати об’єкт
  • Ви майже впевнені, що Вам ніколи не доведеться змінювати спосіб доступу до властивостей, як зазначено вище
  • ви не проти послухати ниття (або насмішку) про те, як ваш код недостатньо "об’єктно-орієнтований"

+1 за перелік і майже -1 за ваше ім’я :) Зрештою, всі знають, що це моя думка, яка є правильною. Кожен, звичайно, визначається у розумінні Дискосвіту.
екстранеон

12
Перевірку меж та перевірку введення можна виконати в конструкторі. Якщо введення недійсне або не має обмежень, я не хочу створювати об’єкт.
Йонас,

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

4
Ви не згадуєте про різьбу з незмінними POJO, що, безумовно, є однією з найбільших причин їх використання. Іншим важливим елементом є простота - коли об’єкт не змінюється, з ним легше працювати в довгостроковій перспективі. Крім того, з незмінними POJO, вам не потрібно турбуватися про клонування об’єкта загалом, ви можете просто передавати його.
deterb

Як ви будуєте графіки об’єктів, які можуть мати кругові посилання, щоб ці об’єкти були незмінними? Чи можливо це? Наприклад, я хочу, щоб класи A і B були незмінними, але A потрібне посилання на B, а B - посилання на A. Чи є спосіб зробити це?
chakrit

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, нащадком клієнта.

Якби була проміжна точка (створити, встановити властивості, зробити незмінною), можливо, фреймворки заохочували б більш незмінний підхід.

У будь-якому випадку я пропоную мислити в незмінних об'єктах як "лише для читання 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, зараз дуже популярні (і з поважної причини), і вони справді найкраще працюють з квасолею.

Тому я не вважаю незмінність поганою, але це, безумовно, обмежило б ваші варіанти інтеграції з поточними структурами.

РЕДАГУВАТИ Коментарі підказують мені трохи пояснити свою відповідь.

Звичайно, є проблемні області, де незмінність дуже корисна і справді використовується. Але я думаю, що нинішнє значення за замовчуванням здається мінливим, оскільки саме це в основному очікується, і незмінне лише у тому випадку, якщо це має явну перевагу.

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


2
Ідея IoC / DI заснована на встановленні / введенні стану, ось чому незмінність не так популярна навесні. Однак Joda-Time є хорошим прикладом незмінності, зробленої правильно.
луногодов

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

6
@lunohodov: Я використовував Google Guice для DI / IoC, і немає проблем зробити ін'єкцію залежностей у конструктор.
Йонас,

Загалом, найпростіше працювати з речами (особливо в багатопотоковому середовищі), якщо дані зберігаються за допомогою незмінних колись створених посилань на змінні об'єкти або змінних посилань на незмінні об'єкти. Наявність вільно змінюваних посилань на змінні об’єкти ускладнює ситуацію. Зверніть увагу, що якщо Xі Yпосилаються на один і той же об'єкт, і хтось хоче змінитись X.something, можна зберегти ідентичність X та змінити Y.something, або залишити Y.somethingнезмінним та змінити ідентичність X, але не можна зберегти ідентичність X та стан Y під час зміни стан X.
суперкіт

Ті, хто натискає на використання незмінних об'єктів, іноді не усвідомлюють важливості ідентичності об'єкта, а також того факту, що змінні об'єкти можуть мати незмінну ідентичність таким чином, що незмінні об'єкти не можуть.
supercat

0

Незмінна з точки зору програмування на Java: Щось, що колись було створено, ніколи не повинно мати змін стану, як очікуваних, так і несподіваних!

Цей прийом корисний у захисному програмуванні, коли інший об'єкт не може викликати зміни в стані.

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

Приклади, коли ви очікуєте змін: насправді не потрібна незмінність, оскільки вона буде перешкоджати правильним процедурам програмування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.