Dlaczego nie powinienem używać niezmiennych POJO zamiast JavaBeans?


79

Zaimplementowałem kilka aplikacji Java, jak dotąd tylko aplikacje desktopowe. Wolę używać niezmiennych obiektów do przekazywania danych w aplikacji zamiast używać obiektów z mutatorami (ustawiającymi i pobierającymi ), zwanymi również JavaBeans.

Ale w świecie Javy wydaje się, że znacznie częściej używa się JavaBeans i nie rozumiem, dlaczego powinienem ich używać. Osobiście kod wygląda lepiej, jeśli zajmuje się tylko niezmiennymi obiektami, zamiast stale mutować stan.

Niezmienne obiekty są również zalecane w pozycji 15: Minimalizuj zmienność , Efektywna Java 2ed .

Gdybym miał obiekt Personzaimplementowany jako JavaBean , wyglądałoby to tak:

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;
    }
}

I to samo Personzaimplementowane jako niezmienny obiekt:

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;
    }
}

Lub bliżej litery structC:

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

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

Mogłem również mieć metody pobierające w niezmiennym obiekcie, aby ukryć szczegóły implementacji. Ale ponieważ używam go tylko jako struct, wolę pominąć „pobierające” i zachować prostotę.

Po prostu nie rozumiem, dlaczego lepiej jest używać JavaBeans, lub czy mogę i powinienem kontynuować korzystanie z moich niezmiennych POJO?

Wydaje się, że wiele bibliotek Java ma lepszą obsługę JavaBeans, ale może z czasem popularniejsze staje się większe wsparcie dla niezmiennych POJO?


1
Myślę, że masz na myśli Punkt 15: Minimalizuj zmienność , a nie * Minimalizuj niezmienność ”
mat b

@matt: Dzięki =) Zaktualizowałem go teraz.
Jonas

7
Twoje podejście do niezmienności jest świetne, oszczędza wiele problemów z wątkami. +1
whiskeysierra

1
Potrzebowałbyś domyślnego konstruktora, jeśli używasz JPA
Neil McGuigan

Odpowiedzi:


78

Preferuj JavaBeans, kiedy

  • musisz wchodzić w interakcje ze środowiskami, które ich oczekują
  • masz wiele właściwości, dla których wykonywanie całej inicjalizacji podczas tworzenia instancji byłoby niewygodne
  • masz stan, który jest drogi lub niemożliwy do skopiowania z jakiegoś powodu, ale wymaga mutacji
  • myślisz, że w pewnym momencie będziesz musiał zmienić sposób uzyskiwania dostępu do właściwości (np. przejście z przechowywanych do obliczonych wartości, autoryzacja dostępu itp.)
  • chcesz dostosować się do standardów kodowania, które bezmyślnie twierdzą, że używanie JavaBeans jest bardziej "obiektowe"

Preferuj niezmienne POJO, kiedy

  • masz niewielką liczbę prostych właściwości
  • nie musisz wchodzić w interakcje ze środowiskami zakładającymi konwencje JavaBean
  • kopiowanie stanu podczas klonowania obiektu jest łatwe (lub przynajmniej możliwe)
  • w ogóle nie planujesz klonowania obiektu
  • jesteś prawie pewien, że nigdy nie musisz modyfikować sposobu uzyskiwania dostępu do właściwości, jak powyżej
  • nie masz nic przeciwko narzekaniu (lub szyderstwom) na temat tego, że Twój kod nie jest wystarczająco „zorientowany obiektowo”

+1 za wpis i prawie -1 za Twoje imię :) W końcu wszyscy wiedzą, że to moja opinia jest prawidłowa. Oczywiście każdy jest definiowany w sensie Świata Dysku.
extraneon

12
W konstruktorze można wykonać sprawdzanie granic i walidację danych wejściowych. Jeśli dane wejściowe są nieprawidłowe lub poza zakresem, nie chcę tworzyć obiektu.
Jonas

24
Twój drugi punkt dotyczący fasoli nie jest na korzyść fasoli, ale na korzyść wzorca Konstruktora. Nadal możesz skonstruować niezmienny obiekt.

4
Nie wspominasz o wątkach z niezmiennymi POJO, co jest zdecydowanie jednym z największych powodów, dla których warto ich używać. Kolejną ważną rzeczą jest prostota - gdy obiekt się nie zmienia, na dłuższą metę łatwiej z nim pracować. Ponadto, dzięki niezmiennym POJO, nie musisz się ogólnie martwić o klonowanie obiektu, możesz go po prostu przekazać.
zakończenie

Jak budować wykresy obiektów, które mogą mieć odwołania cykliczne, tak aby te obiekty były niezmienne? czy to możliwe? Na przykład chcę, aby klasy A i B były niezmienne, ale A potrzebuje odniesienia do B, a B potrzebuje odniesienia do A. Czy istnieje sposób, aby to zrobić?
chakrit

39

Zaskoczyło mnie, że słowo Wątek nie pojawiło się nigdzie w tej dyskusji.

Jedną z głównych zalet niezmiennych klas jest to, że są one z natury bardziej bezpieczne dla wątków ze względu na brak mutowalnego, wspólnego stanu.

Nie tylko ułatwia to kodowanie, ale także daje dwie korzyści związane z wydajnością jako efekt uboczny:

  • Mniejsza potrzeba synchronizacji.

  • Większy zakres stosowania zmiennych końcowych, co może ułatwić późniejsze optymalizacje kompilatora.

Naprawdę staram się przejść do niezmiennych obiektów, a nie klas w stylu JavaBean. Ujawnianie wnętrzności obiektów za pomocą metod pobierających i ustawiających prawdopodobnie nie powinno być domyślnym wyborem.


18

To zależy od tego, co próbujesz zrobić. Jeśli pracujesz z trwałą warstwą i pobierasz jakiś wiersz z bazy danych do POJO i chcesz zmienić właściwość i zapisać ją z powrotem, użycie stylu JavaBean byłoby lepsze, zwłaszcza jeśli masz wiele właściwości.

Weź pod uwagę, że twoja osoba ma wiele pól, takich jak imię, drugie imię, nazwisko, data urodzenia, członkowie rodziny, wykształcenie, praca, wynagrodzenie itp.

Tak się składa, że ​​ta osoba jest kobietą, która właśnie wyszła za mąż i zgodziła się na zmianę nazwiska, a Ty musisz zaktualizować bazę danych.

Jeśli używasz niezmiennego POJO, pobierasz reprezentujący ją obiekt Person, a następnie tworzysz nowy obiekt Person, do którego przekazujesz wszystkie właściwości, których nie zmieniłeś, takimi, jakie są, oraz nowe nazwisko i zapisujesz je.

Gdyby to był komponent Java, możesz po prostu zrobić setLastName () i zapisać go.

To „Minimalizuj zmienność”, a nie „nigdy nie używaj zmiennych obiektów”. Niektóre sytuacje działają lepiej z obiektami zmiennymi, naprawdę twoim zadaniem jest zdecydować, czy uczynienie obiektu zmiennym będzie lepiej pasować do twojego programu, czy nie. Nie powinieneś zawsze mówić „musisz używać niezmiennych obiektów”, zamiast tego sprawdź, ile klas możesz uczynić niezmiennymi, zanim zaczniesz robić sobie krzywdę.


1
Ale twój przykład prawie nigdy nie dotyczy tego, co trzeba zrobić w „prawdziwych” zastosowaniach. Zwykle zaktualizowany obiekt danych musi zostać przekazany do struktury walidacji (Hibernate Validator, Spring Validator itp.), W której obiekt jest poddawany różnym operacjom, prawdopodobnie napisanym przez różnych programistów. Jeśli obiekt jest zmienny, nie wiesz, czy pracujesz z tymi samymi danymi, które zostały przekazane (jeśli jakaś walidacja wykonana przed twoją zmutowała obiekt w miejscu). Sprawia, że ​​proces walidacji jest mniej determinujący, bardziej zależny od ścisłego porządku i niemożliwy do porównania.
dsmith

Ponadto „prawdziwa” aplikacja będzie prawdopodobnie miała jakiś rodzaj pamięci podręcznej lub rozproszonej pamięci podręcznej (EHCache itp.), Która zawiera obiekty domeny. Jeśli wiesz, że obiekty są niezmienne, nie musisz ponosić kosztów związanych z kopiowaniem podczas odczytu. Możesz bezpiecznie pobrać i używać obiektu w pamięci bez martwienia się o integralność pamięci podręcznej. IMHO, dla tego rodzaju obiektów domeny biznesowej, zawsze potrzebujesz niezmiennych obiektów. Mutowalne obiekty są OK dla zakresu lokalnego i kilku innych wyspecjalizowanych przypadków, do których nie mam znaków, aby się tutaj dostać.
dsmith

7

Podsumowując inne odpowiedzi myślę, że:

  • Niezmienność ułatwia poprawność (struktury mogą być przekazywane przez referencje i wiesz, że nic nie zostanie zniszczone przez wadliwego / złośliwego klienta) i prostota kodu
  • Mutowalność ułatwia jednorodność : Spring i inne frameworki tworzą obiekt bez argumentów, ustawiają właściwości obiektu i voi là . Ułatw też interfejsy, używając tej samej klasy do podawania danych i zapisywania modyfikacji (nie potrzebujesz, get(id): Clienta save(MutableClient)będąc MutableClientem, potomkiem Client.

Gdyby istniał punkt pośredni (tworzenie, ustawianie właściwości, dokonywanie niezmienności), być może struktury zachęcałyby bardziej do niezmiennego podejścia.

W każdym razie proponuję myśleć o obiektach niezmiennych jako „tylko do odczytu Java Beans”, podkreślając, że jeśli jesteś dobrym chłopcem i nie dotykasz tej niebezpiecznej metody setProperty, wszystko będzie dobrze.


Zgodziłbym się, że najlepszym z obu światów byłoby przejście bibliotek i frameworków do niezmiennego podejścia. Zalety niezmienności są wewnętrzne, podczas gdy zalety mutowalności są przypadkowe.
dsmith

3

Od Java 7 możesz mieć niezmienne ziarna, najlepsze z obu światów. Użyj adnotacji @ConstructorProperties w swoim konstruktorze.

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

Szczerze mówiąc, nie sądzę, aby niezmienne przedmioty stały się tak popularne.

Widzę zalety, ale frameworki takie jak Hibernate i Spring są obecnie bardzo modne (i nie bez powodu) i naprawdę działają najlepiej z fasolą.

Więc nie sądzę, że niezmienność jest zła, ale z pewnością ograniczyłaby twoje opcje integracji z obecnymi frameworkami.

EDYTOWAĆ Komentarze skłaniają mnie do nieco wyjaśnienia mojej odpowiedzi.

Z pewnością istnieją obszary problemowe, w których niezmienność jest bardzo użyteczna i rzeczywiście jest używana. Ale myślę, że obecna wartość domyślna wydaje się być zmienna, ponieważ jest to najbardziej oczekiwane i niezmienne tylko wtedy, gdy ma to wyraźną przewagę.

I chociaż rzeczywiście jest możliwe użycie konstruktorów z argumentami w Spring, wydaje się, że jest to zamierzone jako sposób na użycie starego i / lub zewnętrznego kodu z pięknym, zupełnie nowym kodem Springa. Przynajmniej to, co wyłowiłem z dokumentacji.


2
Idea IoC / DI opiera się na ustawieniu / stanie wtrysku, dlatego niezmienność nie jest tak popularna na wiosnę. Jednak Joda-Time jest dobrym przykładem niezmienności wykonanej właściwie.
lunohodov

Możesz użyć argumentów konstruktora, aby wstrzyknąć zależności również za pomocą spring, po prostu wydaje się, że niewielu ludzi to robi (prawdopodobnie dlatego, że gdy masz dużo zależności, posiadanie konstruktora z ponad 10 parametrami jest przerażające).
Andrei Fierbinteanu

6
@lunohodov: Użyłem Google Guice do DI / IoC i nie ma problemu z wykonaniem wstrzyknięcia zależności do konstruktora.
Jonas

Ogólnie rzecz biorąc, najłatwiej jest pracować z rzeczami (szczególnie w środowisku wielowątkowym), jeśli dane są przechowywane przy użyciu niezmiennych, raz utworzonych odwołań do obiektów modyfikowalnych lub odniesień do obiektów niezmiennych. Posiadanie dowolnie modyfikowalnych odniesień do zmiennych obiektów komplikuje sprawę. Zauważ, że jeśli Xi Yodnosisz się do tego samego obiektu i chcesz zmienić X.something, możesz zachować tożsamość X i zmienić Y.something, lub pozostawić Y.somethingniezmienioną i zmienić tożsamość X, ale nie można zachować tożsamości X i stanu Y podczas zmiany stan X.
supercat

Ci, którzy naciskają na używanie niezmiennych obiektów, czasami nie dostrzegają znaczenia tożsamości obiektu oraz faktu, że zmienne obiekty mogą mieć niezmienną tożsamość w sposób, w jaki niezmienne obiekty nie mogą.
supercat

0

Niezmienne w zakresie programowania w Javie: coś, co raz stworzone, nigdy nie powinno mieć zmiany stanu, zarówno oczekiwanej, jak i nieoczekiwanej!

Ta technika jest przydatna w programowaniu obronnym, w którym inna jednostka nie może wywołać zmiany stanu.

Przykłady, w których nie spodziewasz się zmiany: Systemy zewnętrzne (jedno- lub wielowątkowe), które uzyskują odniesienie z Twojej warstwy i działają na obiekcie i świadomie lub nieświadomie go zmieniają. Teraz może to być POJO, kolekcja lub odniesienie do obiektu i nie spodziewasz się zmiany w tym lub chcesz chronić dane. Zdecydowanie uczyniłbyś przedmiot niezmiennym jako technika obronna

Przykłady, w których spodziewasz się zmiany: Naprawdę nie potrzebujesz niezmienności, ponieważ przeszkadza to we właściwych procedurach programowania.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.