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.
object-oriented
immutability
ahsteele
źródło
źródło
Odpowiedzi:
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!”
źródło
Point
klasa 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.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.
źródło
final
,const
itp zajmuje trochę dodatkowego wysiłku ... chyba, że utworzenie szablonu kod :-)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
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).
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?
źródło
Relationship(a, b)
; w momencie tworzenia relacji, oba podmiotya
ib
już istnieją, a sam związek jest również niezmienna. Nie twierdzę, że takie podejście jest praktyczne w Javie; tylko to jest możliwe.R
jestRelationship(a,b)
i jednoa
i drugie ib
są niezmienne, to ania
nieb
będą miały odniesienia doR
. Aby to zadziałało, odwołanie musiałoby być przechowywane gdzie indziej (np. Klasa statyczna). Czy dobrze rozumiem twoją intencję?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ą.
źródło
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.
źródło
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.
źródło
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.
źródło
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
AllTrucks
zawiera 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 zmienneBobsTruck
moż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, takAllTrucks
aby 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
BobsTruck
zawiera bezpośrednie odwołanie do obiektu # 24601, to jeśli kod, który aktualizuje,AllTrucks
również się aktualizujeBobsTruck
, przestanie reprezentować bieżący stan ciężarówki Boba. Zauważ też, że jeśli nieBobsTruck
jest przechowywany w jakiejś formie obiektu zmiennego, jedynym sposobem, aby aktualizowany kodAllTrucks
mógł go zaktualizować, byłby to kod zaprogramowany.Jeśli ktoś chce mieć możliwość
BobsTruck
obserwowania stanu ciężarówki Boba przy jednoczesnym zachowaniu niezmienności wszystkich obiektów, może miećBobsTruck
niezmienną funkcję, która, biorąc pod uwagę wartość, któraAllTrucks
ma 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 w
AllTrucks
a 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żywaCompareExchange
do aktualizacjiAllTrucks
tylko wtedy, gdy się nie zmienił i reaguje na awarięCompareExchange
przez 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,
CompareExchange
pę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.źródło
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 .
źródło
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:
O ile rozumiem, ludzie używają zmiennych obiektów, ponieważ wciąż mieszają OOP z imperatywnym programowaniem proceduralnym.
źródło
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 :
Możesz zminimalizować # 1 i # 2 za pomocą adnotacji, takich jak
@ConstructorProperties
i stworzenie innego modyfikowalnego obiektu konstruktora (zwykle płynnie), aby utworzyć niezmienny obiekt.źródło
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.
źródło
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ę.
źródło
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.
źródło
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 .
źródło
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.
źródło
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.
źródło
new ReportBuilder().font("Arial").orientation("landscape").build()
withFont
zwraca aReport
.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:
źródło
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ść.
źródło
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:
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.
źródło
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:
źródło
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.
źródło
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.
źródło
final
jest w rzeczywistości około 1/4 kroku w kierunku niezmienności. Nie rozumiem, dlaczego o tym wspominasz.final
. Przywołanie go w tym kontekście wywołuje naprawdę dziwne połączeniefinal
z „mutowalnym”, kiedy celemfinal
jest zapobieganie niektórym rodzajom mutacji. (BTW, nie moja opinia).