Jeśli niezmienne obiekty są dobre, dlaczego ludzie wciąż tworzą obiekty zmienne? [Zamknięte]


250

Jeśli niezmienne obiekty¹ są dobre, proste i oferują korzyści w programowaniu współbieżnym, dlaczego programiści wciąż tworzą zmienne obiekty²?

Mam cztery lata doświadczenia w programowaniu w Javie i, jak widzę, pierwszą rzeczą, którą ludzie robią po utworzeniu klasy, jest generowanie getterów i setterów w IDE (dzięki czemu jest zmienna). Czy brakuje świadomości lub czy w większości scenariuszy możemy uniknąć używania obiektów zmiennych?


¹ Niezmienny obiekt to obiekt, którego stanu nie można zmienić po jego utworzeniu.
² Zmienny obiekt to obiekt, który można modyfikować po jego utworzeniu.


42
Myślę, że poza uzasadnionymi przyczynami (jak wspomniał Péter poniżej), „leniwy programista” jest częstszym powodem niż „głupi” programista. A przed „głupim programistą” jest także „niedoinformowany programista”.
Joachim Sauer

201
Na każdego ewangelicznego programistę / blogera przypada 1000 zapalonych czytelników blogów, którzy od razu wymyślają na nowo i przyjmują najnowsze techniki. Na każdego z nich jest 10.000 programistów z nosa do kamienia szlifierskiego, którzy wykonują dzień pracy i wyjmują produkt za drzwi. Ci faceci używają sprawdzonych i sprawdzonych technik, które działały dla nich od lat. Czekają, aż nowe techniki zostaną powszechnie przyjęte i pokażą rzeczywiste korzyści przed ich zastosowaniem. Nie nazywaj ich głupcami, a oni nie są leniwi, zamiast tego nazywaj ich „zajętymi”.
Binary Worrier

22
@BinaryWorrier: niezmienne obiekty nie są niczym nowym. Być może nie były intensywnie wykorzystywane do obiektów domenowych, ale Java i C # miały je od samego początku. Ponadto: „leniwy” nie zawsze jest złym słowem, niektóre rodzaje „leniwych” są absolutną zaletą dla programisty.
Joachim Sauer

11
@Jachachim: Myślę, że to dość oczywiste, że „leniwy” został użyty w powyższym sensie pejoratywnym :) Ponadto, Niezmienne Przedmioty (takie jak Lambda Calculus i OOP z powrotem za dnia - tak, jestem stary) nie muszą być nowe nagle stać się smakiem miesiąca . Nie twierdzę, że są złą rzeczą (nie są) lub że nie mają swojego miejsca (oczywiście mają), po prostu uspokójcie się, bo nie słyszeli najnowszego Dobrego Słowa i zostałeś nawrócony jak żarliwie (nie obwiniając cię za „leniwy” komentarz, wiem, że próbowałeś go złagodzić).
Binary Worrier

116
-1, niezmienne obiekty nie są „dobre”. Po prostu mniej lub bardziej odpowiednie dla konkretnych sytuacji. Każdy, kto mówi ci jedną lub drugą technikę, jest obiektywnie „dobry” lub „zły” w stosunku do innej w każdej sytuacji, sprzedaje ci religię.
GrandmasterB

Odpowiedzi:


326

Zarówno zmienne, jak i niezmienne obiekty mają swoje własne zastosowania, zalety i wady.

Niezmienne obiekty rzeczywiście w wielu przypadkach upraszczają życie. Są one szczególnie odpowiednie dla typów wartości, w których obiekty nie mają tożsamości, więc można je łatwo zastąpić. I mogą sprawić, że współbieżne programowanie stanie się bezpieczniejsze i czystsze (większość powszechnie znanych błędów współbieżności jest ostatecznie powodowana przez zmienny stan współdzielony między wątkami). Jednak w przypadku dużych i / lub złożonych obiektów tworzenie nowej kopii obiektu dla każdej pojedynczej zmiany może być bardzo kosztowne i / lub uciążliwe. W przypadku obiektów o wyraźnej tożsamości zmiana istniejących obiektów jest znacznie prostsza i intuicyjna niż tworzenie nowej, zmodyfikowanej kopii.

Pomyśl o postaci z gry. W grach szybkość ma najwyższy priorytet, więc reprezentowanie postaci w grze za pomocą obiektów zmiennych najprawdopodobniej sprawi, że gra będzie działać znacznie szybciej niż alternatywna implementacja, w której nowa kopia postaci gry pojawia się przy każdej drobnej zmianie.

Co więcej, nasza percepcja świata rzeczywistego nieuchronnie opiera się na obiektach zmiennych. Kiedy tankujesz samochód na stacji benzynowej, przez cały czas postrzegasz go jako ten sam obiekt (tzn. Jego tożsamość jest zachowywana, a jego stan się zmienia) - nie tak, jakby stary samochód z pustym zbiornikiem został zastąpiony nowym przypadki samochodów, w których ich czołg jest coraz bardziej pełny. Kiedy więc modelujemy jakąś domenę świata rzeczywistego w programie, zazwyczaj łatwiej i łatwiej jest wdrożyć model domeny przy użyciu obiektów zmiennych do reprezentowania bytów świata rzeczywistego.

Poza tymi wszystkimi uzasadnionymi przyczynami, niestety, najbardziej prawdopodobną przyczyną, dla której ludzie wciąż tworzą zmienne obiekty, jest bezwładność umysłu, czyli opór wobec zmian. Zwróć uwagę, że większość dzisiejszych programistów została przeszkolona na długo zanim niezmienność (i zawierający paradygmat, programowanie funkcjonalne) stały się „modne” w swojej strefie wpływów i nie aktualizują swojej wiedzy na temat nowych narzędzi i metod naszego handlu - w rzeczywistości wielu z nas ludzi pozytywnie opiera się nowym pomysłom i procesom. „Programuję w ten sposób od nn lat i nie dbam o najnowsze głupie mody!”


27
Zgadza się. Szczególnie w programowaniu GUI, obiekty zmienne są bardzo przydatne.
Florian Salihovic

23
To nie tylko opór, jestem pewien, że wielu deweloperów chciałoby wypróbować najnowsze i najlepsze, ale jak często pojawiają się nowe projekty w środowisku przeciętnego dewelopera, w którym mogą zastosować te nowe praktyki? Nigdy nikt nie może napisać projektu hobby, aby wypróbować niezmienny stan.
Steven Evers

29
Dwa małe zastrzeżenia do tego: (1) Weź ruchomą postać z gry. Na przykład Pointklasa w .NET jest niezmienna, ale tworzenie nowych punktów w wyniku zmian jest łatwe i dlatego niedrogie. Animowanie niezmiennej postaci może być bardzo tanie przez oddzielenie „ruchomych części” (ale tak, niektóre aspekty są wtedy zmienne). (2) „duże i / lub złożone obiekty” mogą bardzo dobrze być niezmienne. Ciągi są często duże i zwykle korzystają z niezmienności. Kiedyś przepisałem złożoną klasę graficzną, aby była niezmienna, dzięki czemu kod jest prostszy i wydajniejszy. W takich przypadkach kluczem jest posiadanie modyfikowalnego konstruktora.
Konrad Rudolph

4
@KonradRudolph, dobre punkty, dzięki. Nie chciałem wykluczyć użycia niezmienności w złożonych obiektach, ale prawidłowe i wydajne wdrożenie takiej klasy wcale nie jest trywialnym zadaniem, a dodatkowy wysiłek nie zawsze może być uzasadniony.
Péter Török

6
Masz rację, jeśli chodzi o stan kontra tożsamość. Właśnie dlatego Rich Hickey (autor Clojure) rozdzielił ich w Clojure. Można argumentować, że samochód, który masz z 1/2 zbiornikiem gazu, nie jest tym samym samochodem, co ten z 1/4 zbiornikiem paliwa. Mają tę samą tożsamość, ale nie są takie same, każdy „tyknięcie” czasu naszej rzeczywistości tworzy klon każdego obiektu w naszym świecie, a nasze mózgi po prostu łączą je ze wspólną tożsamością. Clojure ma referencje, atomy, agentów itp. Do reprezentowania czasu. Oraz mapy, wektory i listy dla aktualnego czasu.
Timothy Baldridge

130

Myślę, że wszyscy przegapiliście najbardziej oczywistą odpowiedź. Większość programistów tworzy zmienne obiekty, ponieważ zmienność jest domyślna w imperatywnych językach. Większość z nas ma lepsze rzeczy do roboty z naszym czasem niż ciągłe modyfikowanie kodu z dala od wartości domyślnych - bardziej poprawne czy nie. Niezmienność nie jest panaceum bardziej niż jakiekolwiek inne podejście. To sprawia, że ​​niektóre rzeczy są łatwiejsze, ale inne znacznie trudniejsze, jak niektóre odpowiedzi już wskazywały.


9
W większości współczesnych IDE, które znam, potrzeba praktycznie tyle samo wysiłku, aby wygenerować tylko gettery, tak jak i generatory zarówno getterów, jak i settererów. Chociaż prawdą jest, że dodając final, constitp zajmuje trochę dodatkowego wysiłku ... chyba, że utworzenie szablonu kod :-)
Péter Török

10
@ PéterTörök to nie tylko dodatkowy wysiłek - to fakt, że inni koderzy będą chcieli cię zawiesić na obrazie, ponieważ uważają, że Twój styl kodowania jest tak obcy ich doświadczeniu. To również zniechęca do tego rodzaju rzeczy.
Onorio Catenacci

5
Można temu zaradzić poprzez większą komunikację i edukację, np. Wskazane jest, aby porozmawiać lub przedstawić kolegom z zespołu informacje na temat nowego stylu kodowania przed faktycznym wprowadzeniem go do istniejącej bazy kodu. Prawdą jest, że dobry projekt ma wspólny styl kodowania, który nie jest tylko połączeniem stylów kodowania preferowanych przez każdego (byłego i obecnego) członka projektu. Tak więc wprowadzenie lub zmiana idiomów kodowania powinna być decyzją zespołu.
Péter Török

3
@ PéterTörök W języku imperatywnym zmienne obiekty są zachowaniem domyślnym. Jeśli chcesz tylko niezmiennych obiektów, najlepiej przejść na język funkcjonalny.
emory

3
@ PéterTörök Trochę naiwne jest zakładanie, że włączenie niezmienności do programu wymaga jedynie porzucenia wszystkich seterów. Nadal potrzebujesz sposobu, aby stan programu zmieniał się stopniowo, a do tego potrzebujesz budowniczych, niezmienności popsicle lub zmiennych serwerów proxy, z których wszystkie wymagają około miliarda razy wysiłku po prostu porzucenia seterów.
Asad Saeeduddin,

49
  1. Jest miejsce na zmienność. Zasady projektowania sterowane domenami zapewniają solidne zrozumienie tego, co powinno być zmienne, a co niezmienne. Jeśli się nad tym zastanowić, uświadomicie sobie, że nie jest możliwe wyobrażenie sobie systemu, w którym każda zmiana stanu na obiekt wymaga jego zniszczenia i ponownego złożenia oraz każdego obiektu, który się do niego odwołuje. W przypadku złożonych systemów może to łatwo doprowadzić do całkowitego wyczyszczenia i przebudowania grafu obiektowego całego systemu

  2. Większość programistów nie robi niczego, w przypadku gdy wymagania dotyczące wydajności są na tyle znaczące, że muszą skupić się na współbieżności (lub wielu innych kwestiach, które są powszechnie uważane za dobre praktyki przez poinformowanych).

  3. Są rzeczy, których po prostu nie da się zrobić z niezmiennymi obiektami, na przykład relacje dwukierunkowe. Po ustawieniu wartości powiązania dla jednego obiektu zmienia się jego tożsamość. Tak więc ustawiasz nową wartość na drugim obiekcie i ona również się zmienia. Problem polega na tym, że odwołanie do pierwszego obiektu nie jest już ważne, ponieważ utworzono nową instancję reprezentującą obiekt za pomocą odwołania. Kontynuacja tego spowodowałaby nieskończone regresje. Zrobiłem małe studium przypadku po przeczytaniu twojego pytania, oto jak to wygląda. Czy masz alternatywne podejście, które pozwala na taką funkcjonalność przy zachowaniu niezmienności?

        public class ImmutablePerson { 
    
         public ImmutablePerson(string name, ImmutableEventList eventsToAttend)
         {
              this.name = name;
              this.eventsToAttend = eventsToAttend;
         }
         private string name;
         private ImmutableEventList eventsToAttend;
    
         public string Name { get { return this.name; } }
    
         public ImmutablePerson RSVP(ImmutableEvent immutableEvent){
             // the person is RSVPing an event, thus mutating the state 
             // of the eventsToAttend.  so we need a new person with a reference
             // to the new Event
             ImmutableEvent newEvent = immutableEvent.OnRSVPReceived(this);
             ImmutableEventList newEvents = this.eventsToAttend.Add(newEvent));
             var newSelf = new ImmutablePerson(name, newEvents);
             return newSelf;
         }
        }
    
        public class ImmutableEvent { 
         public ImmutableEvent(DateTime when, ImmutablePersonList peopleAttending, ImmutablePersonList peopleNotAttending){
             this.when = when;     
             this.peopleAttending = peopleAttending;
             this.peopleNotAttending = peopleNotAttending;
         }
         private DateTime when; 
         private ImmutablePersonList peopleAttending;
         private ImmutablePersonList peopleNotAttending;
         public ImmutableEvent OnReschedule(DateTime when){
               return new ImmutableEvent(when,peopleAttending,peopleNotAttending);
         }
         //  notice that this will be an infinite loop, because everytime one counterpart
         //  of the bidirectional relationship is added, its containing object changes
         //  meaning it must re construct a different version of itself to 
         //  represent the mutated state, the other one must update its
         //  reference thereby obsoleting the reference of the first object to it, and 
         //  necessitating recursion
         public ImmutableEvent OnRSVPReceived(ImmutablePerson immutablePerson){
               if(this.peopleAttending.Contains(immutablePerson)) return this;
               ImmutablePersonList attending = this.peopleAttending.Add(immutablePerson);
               ImmutablePersonList notAttending = this.peopleNotAttending.Contains( immutablePerson ) 
                                    ? peopleNotAttending.Remove(immutablePerson)
                                    : peopleNotAttending;
               return new ImmutableEvent(when, attending, notAttending);
         }
        }
        public class ImmutablePersonList
        {
          private ImmutablePerson[] immutablePeople;
          public ImmutablePersonList(ImmutablePerson[] immutablePeople){
              this.immutablePeople = immutablePeople;
          }
          public ImmutablePersonList Add(ImmutablePerson newPerson){
              if(this.Contains(newPerson)) return this;
              ImmutablePerson[] newPeople = new ImmutablePerson[immutablePeople.Length];
              for(var i=0;i<immutablePeople.Length;i++)
                  newPeople[i] = this.immutablePeople[i];
              newPeople[immutablePeople.Length] = newPerson;
          }
          public ImmutablePersonList Remove(ImmutablePerson newPerson){
              if(immutablePeople.IndexOf(newPerson) != -1)
              ImmutablePerson[] newPeople = new ImmutablePerson[immutablePeople.Length-2];
              bool hasPassedRemoval = false;
              for(var i=0;i<immutablePeople.Length;i++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutablePeople[i] == newPerson;
                 newPeople[i] = this.immutablePeople[hasPassedRemoval ? i + 1 : i];
              }
              return new ImmutablePersonList(newPeople);
          }
          public bool Contains(ImmutablePerson immutablePerson){ 
             return this.immutablePeople.IndexOf(immutablePerson) != -1;
          } 
        }
        public class ImmutableEventList
        {
          private ImmutableEvent[] immutableEvents;
          public ImmutableEventList(ImmutableEvent[] immutableEvents){
              this.immutableEvents = immutableEvents;
          }
          public ImmutableEventList Add(ImmutableEvent newEvent){
              if(this.Contains(newEvent)) return this;
              ImmutableEvent[] newEvents= new ImmutableEvent[immutableEvents.Length];
              for(var i=0;i<immutableEvents.Length;i++)
                  newEvents[i] = this.immutableEvents[i];
              newEvents[immutableEvents.Length] = newEvent;
          }
          public ImmutableEventList Remove(ImmutableEvent newEvent){
              if(immutableEvents.IndexOf(newEvent) != -1)
              ImmutableEvent[] newEvents = new ImmutableEvent[immutableEvents.Length-2];
              bool hasPassedRemoval = false;
              for(var i=0;i<immutablePeople.Length;i++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutableEvents[i] == newEvent;
                 newEvents[i] = this.immutableEvents[hasPassedRemoval ? i + 1 : i];
              }
              return new ImmutableEventList(newPeople);
          }
          public bool Contains(ImmutableEvent immutableEvent){ 
             return this.immutableEvent.IndexOf(immutableEvent) != -1;
          } 
        }
    

2
@AndresF., Jeśli masz inne podejście do utrzymywania złożonych wykresów z relacjami dwukierunkowymi przy użyciu tylko niezmiennych obiektów, chciałbym to usłyszeć. (Zakładam, że możemy się zgodzić, że kolekcja / tablica jest obiektem)
smartcaveman

2
@AndresF., (1) Moje pierwsze zdanie nie było uniwersalne, więc nie było fałszywe. Właściwie podałem przykład kodu wyjaśniający, jak to koniecznie było prawdą w niektórych przypadkach, które zdarzają się bardzo często podczas tworzenia aplikacji. (2) Zasadniczo relacje dwukierunkowe wymagają zmienności. Nie wierzę, że winowajcą jest Java. Jak powiedziałem, z przyjemnością oceniłbym wszelkie konstruktywne alternatywy, które byś sugerował, ale w tym momencie twoje komentarze brzmią podobnie: „Mylisz się, ponieważ tak powiedziałem”.
smartcaveman

14
@smartcaveman About (2), ja również się nie zgadzam: ogólnie „dwukierunkowa relacja” jest matematyczną koncepcją prostopadłą do zmienności. Jak zwykle implementuje się w Javie, wymaga modyfikowalności (zgodziłem się z tym w tej kwestii). Mogę jednak wymyślić alternatywną implementację: klasę relacji między dwoma obiektami z konstruktorem Relationship(a, b); w momencie tworzenia relacji, oba podmioty ai bjuż istnieją, a sam związek jest również niezmienna. Nie twierdzę, że takie podejście jest praktyczne w Javie; tylko to jest możliwe.
Andres F.,

4
@AndresF., Opierając się zatem na tym, co mówisz: Jeśli Rjest Relationship(a,b)i jedno ai drugie i bsą niezmienne, to ani anie bbędą miały odniesienia do R. Aby to zadziałało, odwołanie musiałoby być przechowywane gdzie indziej (np. Klasa statyczna). Czy dobrze rozumiem twoją intencję?
smartcaveman

9
Możliwe jest przechowywanie dwukierunkowego związku dla niezmiennych danych, jak wskazuje lenistwo Chthulhu. Oto jeden ze sposobów, aby to zrobić: haskell.org/haskellwiki/Tying_the_Knot
Thomas Eding

36

Czytałem „czysto funkcjonalne struktury danych” i uświadomiłem sobie, że istnieje całkiem sporo struktur danych, które można znacznie łatwiej zaimplementować przy użyciu zmiennych obiektów.

Aby wdrożyć binarne drzewo wyszukiwania, musisz za każdym razem zwracać nowe drzewo: Twoje nowe drzewo będzie musiało wykonać kopię każdego zmodyfikowanego węzła (niezmodyfikowane gałęzie są udostępniane). Dla twojej funkcji wstawiania nie jest tak źle, ale dla mnie sprawy stały się dość nieefektywne, kiedy zacząłem pracować nad usuwaniem i ponownym równoważeniem.

Inną rzeczą do zrealizowania jest to, że możesz latami pisać kod zorientowany obiektowo i nigdy tak naprawdę nie zdajesz sobie sprawy, jak straszny może być wspólny stan mutable, jeśli twój kod nie jest uruchamiany w sposób, który ujawnia problemy z współbieżnością.


3
Czy to książka Okasaki?
Ślimak mechaniczny

tak. trochę sucho, ale mnóstwo dobrych informacji ...
Paul Sanwald,

4
Zabawne, zawsze myślałem, że czerwone / czarne drzewo Okasakis jest o wiele prostsze. 10 linii lub więcej. Myślę, że największą zaletą jest to, że naprawdę chcesz zachować starą wersję.
Thomas Ahle,

Chociaż ostatnie zdanie było prawdopodobnie prawdziwe w przeszłości, nie jest jasne, że pozostanie prawdziwe w przyszłości, biorąc pod uwagę obecne trendy sprzętowe itp.
jk.

29

Z mojego punktu widzenia jest to brak świadomości. Jeśli spojrzysz na inne znane języki JVM (Scala, Clojure), zmienne obiekty są rzadko widoczne w kodzie i dlatego ludzie zaczynają ich używać w scenariuszach, w których pojedynczy wątek nie jest wystarczający.

Obecnie uczę się Clojure i mam małe doświadczenie w Scali (4 lata + również w Javie), a twój styl kodowania zmienia się z powodu świadomości stanu.


Być może lepszym wyborem byłoby „znane” niż „popularne”.
Den

Tak to prawda.
Florian Salihovic

5
+1: Zgadzam się: po nauczeniu się Scali i Haskell mam tendencję do używania finału w Javie i const w C ++ wszędzie. Używam również obiektów niezmiennych, jeśli to możliwe, i chociaż obiekty zmienne są wciąż bardzo potrzebne, zdumiewające jest to, jak często można używać obiektów niezmiennych.
Giorgio,

5
Przeczytałem ten komentarz po dwóch i pół roku i moje zdanie zmieniło się na korzyść niezmienności. W moim obecnym projekcie (który jest w Pythonie) bardzo rzadko używamy obiektów zmiennych. Nawet nasze trwałe dane są niezmienne: tworzymy nowe rekordy w wyniku niektórych operacji i usuwamy stare, gdy nie są już potrzebne, ale nigdy nie aktualizujemy żadnego rekordu na dysku. Nie trzeba dodawać, że dzięki temu nasza współbieżna, wieloużytkownikowa aplikacja jest znacznie łatwiejsza do wdrożenia i utrzymania do tej pory.
Giorgio

13

Myślę, że jednym z głównych czynników wpływającym został zignorowany: Java Beans w dużym stopniu polegają na określonym stylu mutacji obiektów, oraz (zwłaszcza biorąc pod uwagę źródła) sporo ludzi wydaje się, że jako (lub nawet w ) kanonicznym przykładem jak wszyscy Java powinien być napisany.


4
+1, wzorzec gettera / settera jest używany zbyt często jako domyślna implementacja po pierwszej analizie danych.
Jaap

To chyba ważna kwestia ... „ponieważ tak robią wszyscy inni”… więc musi być słuszna. W przypadku programu „Hello World” jest to prawdopodobnie najłatwiejsze. Zarządzanie zmianami stanu obiektów za pomocą wielu zmiennych właściwości ... jest to trochę trudniejsze niż głębia zrozumienia „Hello World”. 20 lat później jestem bardzo zaskoczony, że szczytem programowania w XX wieku jest pisanie metod getX i setX (jak żmudne) nad każdym atrybutem obiektu bez jakiejkolwiek struktury. To tylko jeden krok od bezpośredniego dostępu do nieruchomości publicznych ze 100% zmiennością.
Darrell Teague,

12

Każdy korporacyjny system Java, nad którym pracowałem w swojej karierze, korzysta z Hibernate lub Java Persistence API (JPA). Hibernacja i JPA zasadniczo dyktują, że twój system używa zmiennych obiektów, ponieważ cała ich przesłanka polega na tym, że wykrywają i zapisują zmiany w twoich obiektach danych. W przypadku wielu projektów łatwość programowania, którą zapewnia Hibernacja, jest bardziej przekonująca niż korzyści wynikające z niezmiennych obiektów.

Oczywiście zmienne obiekty istniały znacznie dłużej niż Hibernacja, więc Hibernacja prawdopodobnie nie jest oryginalną „przyczyną” popularności obiektów zmiennych. Być może popularność obiektów zmiennych pozwoliła Hibernacji rozkwitnąć.

Ale dzisiaj, jeśli wielu młodszych programistów obciąży zęby systemom korporacyjnym za pomocą Hibernacji lub innej ORM, prawdopodobnie nabiorą nawyku używania zmiennych obiektów. Ramy takie jak Hibernacja mogą zwiększać popularność obiektów zmiennych.


Doskonały punkt Aby być wszystkim dla wszystkich, takie frameworki nie mają innego wyboru, jak tylko spaść do najniższego wspólnego mianownika, użyć proxy opartych na odbiciu i uzyskać / ustawić swoją drogę do elastyczności. Oczywiście tworzy to systemy z niewielkimi lub żadnymi regułami zmiany stanu lub wspólnymi środkami, za pomocą których można je niestety wdrożyć. Po wielu projektach nie jestem całkowicie pewien, co jest lepsze pod względem celowości, rozszerzalności i poprawności. Mam tendencję do myślenia o hybrydzie. Dynamiczna wartość ORM, ale z pewnymi definicjami, dla których pola są wymagane i jakie zmiany stanu powinny być możliwe.
Darrell Teague,

9

Główną kwestią, o której jeszcze nie wspomniano, jest to, że możliwość zmiany stanu obiektu umożliwia, aby tożsamość obiektu otaczającego ten stan była niezmienna.

Wiele programów zaprojektowano do modelowania rzeczy z prawdziwego świata, które są z natury zmienne. Załóżmy, że o godzinie 12:51 jakaś zmienna AllTruckszawiera odniesienie do obiektu # 451, który jest rdzeniem struktury danych, która wskazuje, jaki ładunek jest zawarty we wszystkich ciężarówkach floty w tym momencie (12:51), a niektóre zmienne BobsTruckmożna użyć, aby uzyskać odniesienie do obiektu # 24601 wskazuje na obiekt wskazujący, jaki ładunek znajduje się w ciężarówce Boba w tym momencie (12:51). O godzinie 12:52 niektóre ciężarówki (w tym Boba) są ładowane i rozładowywane, a struktury danych są aktualizowane, tak AllTrucksaby zawierały teraz odniesienie do struktury danych, która wskazuje, że ładunek jest we wszystkich ciężarówkach od godziny 12:52.

Co powinno się stać BobsTruck?

Jeśli właściwość „cargo” każdego obiektu ciężarówki jest niezmienna, wówczas obiekt # 24601 na zawsze będzie reprezentował stan, w którym ciężarówka Boba miała o 12:51. Jeśli BobsTruckzawiera bezpośrednie odwołanie do obiektu # 24601, to jeśli kod, który aktualizuje, AllTrucksrównież się aktualizuje BobsTruck, przestanie reprezentować bieżący stan ciężarówki Boba. Zauważ też, że jeśli nie BobsTruckjest przechowywany w jakiejś formie obiektu zmiennego, jedynym sposobem, aby aktualizowany kod AllTrucksmógł go zaktualizować, byłby to kod zaprogramowany.

Jeśli ktoś chce mieć możliwość BobsTruckobserwowania stanu ciężarówki Boba przy jednoczesnym zachowaniu niezmienności wszystkich obiektów, może mieć BobsTruckniezmienną funkcję, która, biorąc pod uwagę wartość, która AllTrucksma lub miała w danym momencie, da stan ciężarówki Boba przy ten czas. Można nawet sprawić, by miał parę niezmiennych funkcji - z których jedna byłaby jak wyżej, a druga zaakceptowałaby odniesienie do stanu floty i nowego stanu ciężarówki, i zwróciłaby odniesienie do nowego stanu floty, który pasowało do starego, tyle że ciężarówka Boba miałaby nowy stan.

Niestety konieczność korzystania z takiej funkcji za każdym razem, gdy chcemy uzyskać dostęp do stanu ciężarówki Boba, może być dość irytująca i uciążliwa. Alternatywnym podejściem byłoby powiedzenie, że obiekt # 24601 zawsze i na zawsze (tak długo, jak ktoś ma do niego odniesienie) reprezentuje bieżący stan ciężarówki Boba. Kod, który będzie chciał wielokrotnie uzyskiwać dostęp do aktualnego stanu ciężarówki Boba, nie musiałby za każdym razem uruchamiać czasochłonnej funkcji - mógłby po prostu wykonać funkcję wyszukiwania, aby dowiedzieć się, że obiekt # 24601 jest ciężarówką Boba, a następnie po prostu dostęp do tego obiektu za każdym razem, gdy chce zobaczyć obecny stan ciężarówki Boba.

Zauważ, że podejście funkcjonalne nie jest pozbawione zalet w środowisku jednowątkowym lub w środowisku wielowątkowym, w którym wątki przeważnie tylko obserwują dane, a nie je zmieniają. Każdy wątek obserwatora, który kopiuje odwołanie do obiektu zawarte wAllTrucksa następnie sprawdza reprezentowane stany ciężarówek, dzięki czemu zobaczy stan wszystkich ciężarówek w momencie, w którym chwycił odniesienie. Za każdym razem, gdy wątek obserwatora chce zobaczyć nowsze dane, może po prostu ponownie pobrać referencję. Z drugiej strony posiadanie całego stanu floty reprezentowanego przez jeden niezmienny obiekt wykluczałoby możliwość jednoczesnego aktualizowania różnych ciężarówek przez dwa wątki, ponieważ każdy wątek pozostawiony własnym urządzeniom wytworzyłby nowy obiekt „stanu floty”, który obejmowałby nowy stan jego ciężarówki i stare stany każdego innego. Poprawność można zapewnić, jeśli każdy wątek używa CompareExchangedo aktualizacji AllTruckstylko wtedy, gdy się nie zmienił i reaguje na awarięCompareExchangeprzez ponowne wygenerowanie obiektu stanu i ponowienie operacji, ale jeśli więcej niż jeden wątek podejmie operację jednoczesnego zapisu, wydajność będzie na ogół gorsza niż w przypadku, gdy wszystkie zapisy zostały wykonane w jednym wątku; im więcej wątków podejmie takie jednoczesne operacje, tym gorsza będzie wydajność.

Jeśli poszczególne obiekty ciężarówki są zmienne, ale mają niezmienne tożsamości , scenariusz wielowątkowy staje się czystszy. Tylko jeden wątek może być dopuszczony do działania w danym momencie na danej ciężarówce, ale wątki działające na różnych ciężarówkach mogą to robić bez ingerencji. Chociaż istnieją sposoby naśladowania takiego zachowania, nawet przy użyciu niezmiennych obiektów (np. Można zdefiniować obiekty „AllTrucks”, aby ustawienie stanu ciężarówki należącej do XXX do SSS wymagało po prostu wygenerowania obiektu o treści „Na [Czas], stan ciężarówki należącej do [XXX] to teraz [SSS]; stan wszystkiego innego to [Stara wartość AllTrucks] ”. Wygenerowanie takiego obiektu byłoby wystarczająco szybkie, aby nawet w przypadku sporu,CompareExchangepętla nie potrwa długo. Z drugiej strony użycie takiej struktury danych znacznie zwiększyłoby czas potrzebny na znalezienie ciężarówki konkretnej osoby. Używanie obiektów zmiennych z niezmiennymi tożsamościami pozwala uniknąć tego problemu.


8

Nie ma dobra ani zła, zależy to tylko od tego, co wolisz. Jest powód, dla którego niektórzy wolą języki, które preferują jeden paradygmat nad drugim, a jeden model danych nad drugim. Zależy to tylko od twoich preferencji i tego, co chcesz osiągnąć (a możliwość łatwego korzystania z obu podejść bez wyobcowania zagorzałych fanów jednej lub drugiej strony jest świętym Graalem, którego szukają niektóre języki).

Myślę, że najlepszym i najszybszym sposobem na udzielenie odpowiedzi na twoje pytanie jest pokonanie zalet i wad niezmienności vs. zmienności .


7

Sprawdź ten post na blogu: http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . Podsumowuje, dlaczego niezmienne obiekty są lepsze niż zmienne. Oto krótka lista argumentów:

  • niezmienne obiekty są prostsze w budowie, testowaniu i użyciu
  • naprawdę niezmienne obiekty są zawsze bezpieczne dla wątków
  • pomagają uniknąć czasowego połączenia
  • ich użycie jest wolne od skutków ubocznych (bez kopii obronnych)
  • unika się problemu zmienności tożsamości
  • zawsze mają atomowość awarii
  • są o wiele łatwiejsze do buforowania

O ile rozumiem, ludzie używają zmiennych obiektów, ponieważ wciąż mieszają OOP z imperatywnym programowaniem proceduralnym.


5

W Javie niezmienne obiekty wymagają konstruktora, który weźmie wszystkie właściwości obiektu (lub konstruktor utworzy je z innych argumentów lub wartości domyślnych). Te właściwości należy oznaczyć jako ostateczne .

Istnieją cztery problemy z tym związane głównie z wiązaniem danych :

  1. Metadane odbicia Java Constructors nie zachowują nazw argumentów.
  2. Konstruktory Java (i metody) nie mają nazwanych parametrów (zwanych także etykietami), dlatego mylą się z wieloma parametrami.
  3. Podczas dziedziczenia innego niezmiennego obiektu należy wywołać odpowiednią kolejność konstruktorów. Może to być dość trudne do tego stopnia, że ​​po prostu poddaje się i pozostawia jedno z pól nie-końcowe.
  4. Większość technologii wiązania (takich jak Spring MVC Data Binding, Hibernate itp.) Będzie działać tylko z domyślnymi konstruktorami bez arg (to dlatego, że adnotacje nie zawsze istniały).

Możesz zminimalizować # 1 i # 2 za pomocą adnotacji, takich jak @ConstructorPropertiesi stworzenie innego modyfikowalnego obiektu konstruktora (zwykle płynnie), aby utworzyć niezmienny obiekt.


5

Jestem zaskoczony, że nikt nie wspomniał o korzyściach związanych z optymalizacją wydajności. W zależności od języka kompilator może dokonać optymalizacji w przypadku niezmiennych danych, ponieważ wie, że dane nigdy się nie zmienią. Wszystkie rodzaje rzeczy są pomijane, co daje ogromne korzyści w zakresie wydajności.

Również niezmienne obiekty prawie eliminują całą klasę błędów stanowych.

Nie jest tak duży, jak powinien być, ponieważ jest trudniejszy, nie dotyczy każdego języka i większość ludzi uczy się kodowania imperatywnego.

Odkryłem również, że większość programistów jest zadowolona ze swojego zestawu i często jest odporna na nowe pomysły, których nie do końca rozumieją. Ogólnie ludzie nie lubią zmian.

Pamiętaj też, że stan większości programistów jest zły. Większość programów wykonywanych na wolności jest straszna, a jej przyczyną jest brak zrozumienia i polityki.


4

Dlaczego ludzie korzystają z jakiejkolwiek potężnej funkcji? Dlaczego ludzie używają metaprogramowania, lenistwa lub dynamicznego pisania? Odpowiedzią jest wygoda. Zmienny stan jest bardzo łatwy. Aktualizacja jest tak łatwa w miejscu, a próg wielkości projektu, w którym stan niezmienny jest bardziej produktywny, niż stan zmienny jest dość wysoki, więc wybór nie odstraszy cię przez chwilę.


4

Języki programowania są zaprojektowane do wykonywania przez komputery. Wszystkie ważne elementy składowe komputerów - procesory, pamięć RAM, pamięć podręczna, dyski - można modyfikować. Gdy nie są (BIOS), są naprawdę niezmienne i nie można również tworzyć nowych niezmiennych obiektów.

Dlatego też każdy język programowania zbudowany na niezmiennych obiektach ma lukę reprezentacyjną w jego implementacji. A dla wczesnych języków, takich jak C, była to duża przeszkoda.


1

Bez obiektów zmiennych nie masz stanu. Trzeba przyznać, że jest to dobra rzecz, jeśli można nią zarządzać, a jeśli istnieje jakakolwiek szansa, że ​​obiekt może być przywołany z więcej niż jednego wątku. Ale program będzie raczej nudny. Wiele programów, zwłaszcza serwerów sieciowych, unika ponoszenia odpowiedzialności za obiekty zmienne poprzez zepchnięcie zmienności do baz danych, systemów operacyjnych, bibliotek systemowych itp. W praktyce uwalnia to programistę od problemów ze zmiennością i sprawia, że ​​sieć (i inne) rozwój w przystępnej cenie. Ale zmienność wciąż istnieje.

Zasadniczo istnieją trzy typy klas: normalne, niechronione wątkiem, które muszą być ostrożnie strzeżone i chronione; niezmienne klasy, z których można swobodnie korzystać; oraz zmienne, bezpieczne dla wątków klasy, z których można swobodnie korzystać, ale które muszą być pisane z najwyższą starannością. Pierwszy typ jest kłopotliwy, a najgorsze to te, które uważa się za trzecie. Oczywiście pierwszy typ jest łatwy do napisania.

Zazwyczaj kończę z wieloma normalnymi, zmiennymi klasami, które muszę bardzo uważnie obserwować. W sytuacji wielowątkowej niezbędna synchronizacja spowalnia wszystko, nawet gdy mogę uniknąć śmiertelnego uścisku. Zwykle robię niezmienne kopie tej zmiennej klasy i przekazuję je każdemu, kto może z niej skorzystać. Nowa niezmienna kopia jest potrzebna za każdym razem, gdy orignal mutuje, więc wyobrażam sobie, że czasami mam tam sto kopii oryginału. Jestem całkowicie zależny od Garbage Collection.

Podsumowując, niechronne wątkiem, zmienne obiekty są w porządku, jeśli nie używasz wielu wątków. (Ale wielowątkowość jest wszędzie inflitrująca - bądź ostrożny!) Mogą być bezpiecznie używane w przeciwnym razie, jeśli ograniczysz je do zmiennych lokalnych lub rygorystycznie zsynchronizujesz. Jeśli możesz ich uniknąć, używając sprawdzonego kodu innych osób (DB, wywołania systemowe itp.), Zrób to. Jeśli można używać niezmienny klasy, należy to zrobić. I myślę , że generalnie ludzie albo nie są świadomi problemów z wielowątkowością, albo (rozsądnie) się ich boją i używają wszelkiego rodzaju sztuczek, aby uniknąć wielowątkowości (a raczej zrzucają odpowiedzialność za to gdzie indziej).

Jako PS wyczuwam, że programy pobierające i ustawiające Java wymykają się spod kontroli. Sprawdź to .


7
Niezmienny stan jest nadal stanem.
Jeremy Heiler,

3
@JeremyHeiler: To prawda, ale jest to stan czegoś, co można zmienić. Jeśli nic się nie mutuje, istnieje tylko jeden stan, który jest tym samym, co żaden stan.
RalphChapin

1

Wiele osób miało dobre odpowiedzi, więc chcę wskazać na coś, na co się poruszyłeś, co było bardzo spostrzegawcze i niezwykle prawdziwe i nie zostało wspomniane gdzie indziej.

Automatyczne tworzenie seterów i getterów jest okropnym, okropnym pomysłem, ale jest to pierwszy sposób, w jaki ludzie nastawieni na procedury próbują zmusić OO do swojego sposobu myślenia. Settery i gettery wraz z właściwościami powinny być tworzone tylko wtedy, gdy okaże się, że są potrzebne, a nie domyślnie

W rzeczywistości, chociaż potrzebujesz programów pobierających dość regularnie, jedynym sposobem, w jaki setery lub zapisywalne właściwości powinny istnieć w kodzie, jest wzorzec konstruktora, w którym są one blokowane po całkowitym utworzeniu instancji obiektu.

Wiele klas można modyfikować po utworzeniu, co jest w porządku, po prostu nie powinno się bezpośrednio manipulować jego właściwościami - zamiast tego należy poprosić o manipulowanie jego właściwościami za pomocą wywołań metod z rzeczywistą logiką biznesową (tak, ustawiający jest prawie taki sam rzecz polegająca na bezpośrednim manipulowaniu właściwością)

Teraz tak naprawdę nie dotyczy to kodu / języków w stylu „skryptów”, ale kod, który tworzysz dla kogoś innego i oczekujesz, że inni będą go czytać przez lata. Ostatnio zacząłem robić to rozróżnienie, ponieważ tak bardzo lubię bawić się z Groovy, a cele są ogromne.


1

Zmienne obiekty są używane, gdy trzeba ustawić wielokrotności wartości po utworzeniu wystąpienia obiektu.

Nie powinieneś mieć konstruktora z, powiedzmy, sześcioma parametrami. Zamiast tego modyfikujesz obiekt metodami ustawiającymi.

Przykładem tego jest obiekt Report z ustawieniami czcionki, orientacji itp.

W skrócie: zmienne są przydatne, gdy masz dużo stanu do ustawienia na obiekcie i nie byłoby praktyczne posiadanie bardzo długiej sygnatury konstruktora.

EDYCJA: Wzorzec konstruktora można wykorzystać do zbudowania całego stanu obiektu.


3
jest to przedstawione tak, jakby zmienność i zmiany po utworzeniu wystąpienia były jedynym sposobem na ustawienie wielu wartości. Ze względu na kompletność zauważ, że wzorzec Konstruktora oferuje równe lub nawet potężniejsze możliwości i nie wymaga poświęcenia niezmienności. new ReportBuilder().font("Arial").orientation("landscape").build()
komar

1
„Nie byłoby praktyczne posiadanie bardzo długiej sygnatury konstruktora.”: Zawsze możesz pogrupować parametry w mniejsze obiekty i przekazać je jako parametry konstruktorowi.
Giorgio

1
@ cHao Co zrobić, jeśli chcesz ustawić 10 lub 15 atrybutów? Również nie wydaje się dobrze, że metoda o nazwie withFontzwraca a Report.
Tulains Córdova

1
Jeśli chodzi o ustawienie 10 lub 15 atrybutów, kod do zrobienia tego nie byłby niezręczny, gdyby (1) Java wiedziała, jak pominąć konstrukcję wszystkich tych pośrednich obiektów, a jeśli (2) ponownie, nazwy zostały ustandaryzowane. To nie jest problem z niezmiennością; problem polega na tym, że Java nie wie, jak to zrobić dobrze.
Chao

1
@ cHao Wykonanie łańcucha połączeń dla 20 atrybutów jest brzydkie. Brzydki kod jest zwykle kodem niskiej jakości.
Tulains Córdova

1

Myślę, że używanie zmiennych obiektów wynika z myślenia imperatywnego: obliczasz wynik, zmieniając zawartość zmiennych zmiennych krok po kroku (obliczanie według efektu ubocznego).

Jeśli myślisz funkcjonalnie, chcesz mieć niezmienny stan i reprezentować kolejne stany systemu poprzez zastosowanie funkcji i tworzenie nowych wartości ze starych.

Podejście funkcjonalne może być czystsze i bardziej niezawodne, ale może być bardzo nieefektywne z powodu kopiowania, dzięki czemu chcesz wrócić do wspólnej struktury danych, którą modyfikujesz stopniowo.

Kompromis, który uważam za najbardziej rozsądny, to: zacznij od obiektów niezmiennych, a następnie przełącz się na zmienne, jeśli twoja implementacja nie jest wystarczająco szybka. Z tego punktu widzenia systematyczne używanie zmiennych obiektów od samego początku można uznać za pewnego rodzaju przedwczesną optymalizację : od samego początku wybierasz bardziej wydajną (ale także trudniejszą do zrozumienia i debugowania) implementację.

Dlaczego więc wielu programistów używa zmiennych obiektów? IMHO z dwóch powodów:

  1. Wielu programistów nauczyło się programować przy użyciu imperatywnego (proceduralnego lub obiektowego) paradygmatu, dlatego zmienność jest ich podstawowym podejściem do definiowania obliczeń, tzn. Nie wiedzą, kiedy i jak używać niezmienności, ponieważ nie są z nią zaznajomieni.
  2. Wielu programistów zbyt wcześnie martwi się wydajnością, podczas gdy bardziej efektywne jest najpierw skupienie się na napisaniu programu, który jest funkcjonalnie poprawny, a następnie próba znalezienia wąskich gardeł i zoptymalizowania go.

1
Problemem jest nie tylko prędkość. Istnieje wiele rodzajów przydatnych konstrukcji, których po prostu nie można zaimplementować bez użycia zmiennych typów. Komunikacja między dwoma wątkami wymaga, między innymi, absolutnego minimum, że oba muszą mieć odniesienie do wspólnego obiektu, w którym jeden może umieścić dane, a drugi może je odczytać. Ponadto często semantycznie znacznie wyraźniej jest powiedzieć „Zmień właściwości P i Q tego obiektu” niż powiedzieć „Weź ten obiekt, zbuduj nowy obiekt, który jest taki sam, z wyjątkiem wartości P, a następnie nowy obiekt, który jest tak samo, z wyjątkiem Q ”.
supercat

1
Nie jestem ekspertem od FP, ale komunikację wątków AFAIK (1) można osiągnąć, tworząc niezmienną wartość w jednym wątku i czytając ją w innym (ponownie, AFAIK, to jest podejście Erlanga) (2) AFAIK notacja do ustawiania właściwości niewiele się zmienia (np. w ustawieniu Haskell setProperty wartość rekordu odpowiada Java record.setProperty (wartość)), zmienia się tylko semantyka, ponieważ wynikiem ustawienia właściwości jest nowy niezmienny zapis.
Giorgio

1
„oba muszą mieć odniesienie do współdzielonego obiektu, w którym można umieścić dane, a drugi może je odczytać”: bardziej precyzyjnie, można uczynić obiekt niezmiennym (wszyscy członkowie const w C ++ lub final w Javie) i ustawić całą zawartość w konstruktor. Następnie ten niezmienny obiekt jest przekazywany z wątku producenta do wątku konsumenta.
Giorgio

1
(2) Podałem już wydajność jako powód, aby nie używać niezmiennego stanu. Odnośnie (1), oczywiście mechanizm implementujący komunikację między wątkami wymaga pewnego stanu zmiennego, ale można to ukryć przed modelem programowania, patrz np. Model aktora ( en.wikipedia.org/wiki/Actor_model ). Więc nawet jeśli na niższym poziomie potrzebujesz zmienności do implementacji komunikacji, możesz mieć wyższy poziom abstrakcji, w którym komunikujesz się między wątkami wysyłającymi niezmienne obiekty tam i z powrotem.
Giorgio

1
Nie jestem pewien, czy rozumiem cię w pełni, ale nawet czysty język funkcjonalny, w którym każda wartość jest niezmienna, jest jedna zmienna rzecz: stan programu, tj. Bieżący stos programu i powiązania zmiennych. Ale to środowisko wykonawcze. W każdym razie sugeruję, aby omówić to na czacie, a następnie wyczyścić wiadomości z tego pytania.
Giorgio

1

Wiem, że pytasz o Javę, ale używam zmiennego vs niezmiennego przez cały czas w celu-c. Istnieje niezmienna tablica NSArray i zmienna tablica NSMutableArray. Są to dwie różne klasy napisane specjalnie w zoptymalizowany sposób, aby obsłużyć dokładne użycie. Gdybym musiał utworzyć tablicę i nigdy nie zmieniać jej zawartości, użyłbym NSArray, który jest mniejszym obiektem i jest znacznie szybszy w porównaniu z tablicą zmienną.

Więc jeśli stworzysz obiekt Person, który jest niezmienny, potrzebujesz tylko konstruktora i getterów, dzięki czemu obiekt będzie mniejszy i zużyje mniej pamięci, co z kolei przyspieszy twój program. Jeśli musisz zmienić obiekt po utworzeniu, wówczas zmienny obiekt Person będzie lepszy, aby mógł zmieniać wartości zamiast tworzyć nowy obiekt.

Tak więc: w zależności od tego, co planujesz zrobić z obiektem, wybór opcji mutable vs immutable może mieć ogromną różnicę, jeśli chodzi o wydajność.


1

Oprócz wielu innych podanych tu powodów problemem jest to, że języki głównego nurtu nie obsługują niezmienności. Przynajmniej jesteś karany za niezmienność, ponieważ musisz dodać dodatkowe słowa kluczowe, takie jak const lub final, i musisz użyć nieczytelnych konstruktorów z wieloma, wieloma argumentami lub długimi wzorcami konstruktora.

Jest to o wiele łatwiejsze w językach stworzonych z myślą o niezmienności. Rozważ ten fragment kodu Scala do definiowania klasy Person, opcjonalnie z nazwanymi argumentami i tworzenia kopii ze zmienionym atrybutem:

case class Person(id: String, firstName: String, lastName: String)

val joe = Person("123", "Joe", "Doe")
val spouse = Person(id = "124", firstName = "Mary", lastName = "Moe")
val joeMarried = joe.copy(lastName = "Doe-Moe")

Jeśli więc chcesz, aby programiści przyjęli niezmienność, jest to jeden z powodów, dla których możesz rozważyć przejście na bardziej nowoczesny język programowania.


wydaje się, że nie dodaje to nic istotnego w stosunku do punktów poczynionych i wyjaśnionych w poprzednich 24 odpowiedziach
gnat

@gnat Które z poprzednich odpowiedzi wskazują, że większość popularnych języków nie obsługuje niezmienności? Myślę, że ten punkt po prostu nie został poruszony (sprawdziłem), ale jest to IMHO dość ważna przeszkoda.
Hans-Peter Störr,

ten na przykład szczegółowo wyjaśnia ten problem w Javie. I co najmniej 3 inne odpowiedzi pośrednio się z tym wiążą
komara

0

Niezmienna oznacza, że ​​nie możesz zmienić wartości, a zmienna oznacza, że ​​możesz zmienić wartość, jeśli myślisz w kategoriach prymitywów i obiektów. Obiekty różnią się od prymitywów w Javie tym, że są wbudowane w typy. Prymitywy są wbudowane w typy takie jak int, boolean i void.

Wiele osób uważa, że ​​zmienne pierwotne i obiektowe, które mają przed sobą ostateczny modyfikator, są niezmienne, jednak nie jest to do końca prawdą. Więc ostateczny prawie nie oznacza niezmiennego dla zmiennych. Sprawdź ten link, aby uzyskać przykładowy kod:

public abstract class FinalBase {

    private final int variable; // Unset

    /* if final really means immutable than
     * I shouldn't be able to set the variable
     * but I can.
     */
    public FinalBase(int variable) { 
        this.variable = variable;
    }

    public int getVariable() {
        return variable;
    }

    public abstract void method();
}

// This is not fully necessary for this example
// but helps you see how to set the final value 
// in a sub class.
public class FinalSubclass extends FinalBase {

    public FinalSubclass(int variable) {
        super(variable);
    }

    @Override
    public void method() {
        System.out.println( getVariable() );
    }

    @Override
    public int getVariable() {

        return super.getVariable();
    }

    public static void main(String[] args) {
        FinalSubclass subclass = new FinalSubclass(10);
        subclass.method();
    }
}

-1

Myślę, że dobrym powodem byłoby modelowanie „rzeczywistych” zmiennych obiektów, takich jak okna interfejsu. Niejasno pamiętam, że przeczytałem, że OOP został wymyślony, gdy ktoś próbował napisać oprogramowanie do kontroli operacji portu ładunkowego.


1
Nie mogę jeszcze znaleźć, gdzie przeczytałem o początkach OOP, ale według Wikipedii jakiś zintegrowany regionalny system informacyjny dużej firmy transportowej OOCL został napisany w języku Smalltalk.
Alexey

-2

Java, w wielu przypadkach nakazuje modyfikowalne obiekty, np. Gdy chcesz policzyć lub zwrócić coś odwiedzającemu lub uruchamialnemu, potrzebujesz zmiennych końcowych, nawet bez wielowątkowości.


5
finaljest w rzeczywistości około 1/4 kroku w kierunku niezmienności. Nie rozumiem, dlaczego o tym wspominasz.
cHao

czy rzeczywiście przeczytałeś mój post?
Ralf H

Czy rzeczywiście przeczytałeś pytanie? :) Nie miało to absolutnie nic wspólnego final. Przywołanie go w tym kontekście wywołuje naprawdę dziwne połączenie finalz „mutowalnym”, kiedy celem finaljest zapobieganie niektórym rodzajom mutacji. (BTW, nie moja opinia).
cHao 23.12.12

Nie twierdzę, że gdzieś tu nie ma uzasadnionego powodu. Mówię, że nie wyjaśniasz tego zbyt dobrze. W pewnym sensie częściowo widzę, dokąd idziesz, ale musisz iść dalej. W tej chwili wygląda to na zmieszaną.
cHao

1
W rzeczywistości jest bardzo niewiele przypadków, w których wymagany jest obiekt zmienny . Zdarzają się przypadki, w których kod byłby wyraźniejszy lub w niektórych przypadkach szybszy, dzięki zastosowaniu zmiennych obiektów. Należy jednak wziąć pod uwagę, że zdecydowana większość wzorców projektowych jest w zasadzie tylko w połowie interpretacją programowania funkcjonalnego dla języków, które nie obsługują go natywnie. Zabawne jest to, że FP wymaga modyfikowalności tylko w bardzo wybranych miejscach (mianowicie tam, gdzie muszą wystąpić skutki uboczne), a miejsca te są ogólnie ściśle ograniczone pod względem liczby, wielkości i zakresu.
cHao
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.