Próbuję poradzić sobie z obiektami zmiennymi i niezmiennymi. Korzystanie z obiektów zmiennoprzecinkowych jest bardzo źle odbierane (np. Zwracanie tablicy ciągów z metody), ale mam problem ze zrozumieniem, jakie są tego negatywne skutki. Jakie są najlepsze praktyki dotyczące używania obiektów mutowalnych? Czy należy ich unikać, kiedy tylko jest to możliwe?
oop
immutability
mutable
Alex Angas
źródło
źródło
string
jest niezmienna, przynajmniej w .NET, i myślę, że w wielu innych współczesnych językach.Odpowiedzi:
Cóż, jest kilka aspektów tego.
Zmienne obiekty bez tożsamości referencyjnej mogą powodować błędy w dziwnych momentach. Na przykład rozważmy
Person
fasolę zequals
metodą opartą na wartościach :Person
Instancja zostanie „stracony” na mapie, gdy używany jako klucz, ponieważ jejhashCode
i równość były oparte na wartościach modyfikowalnych. Te wartości zmieniły się poza mapą, a cały hasz stał się przestarzały. Teoretycy lubią harfować na ten temat, ale w praktyce nie wydaje mi się to zbyt dużym problemem.Innym aspektem jest logiczna „sensowność” twojego kodu. Jest to trudny do zdefiniowania termin, obejmujący wszystko, od czytelności po przepływ. Ogólnie rzecz biorąc, powinieneś być w stanie spojrzeć na fragment kodu i łatwo zrozumieć, co robi. Ale co ważniejsze, powinieneś być w stanie przekonać siebie, że robi to, co robi poprawnie . Kiedy obiekty mogą zmieniać się niezależnie w różnych „domenach” kodu, czasami trudno jest śledzić, co jest, gdzie i dlaczego („ upiorne działanie na odległość ”). Jest to koncepcja trudniejsza do zilustrowania, ale często spotykana w większych, bardziej złożonych architekturach.
Wreszcie, zmienne obiekty są zabójcze w współbieżnych sytuacjach. Zawsze, gdy uzyskujesz dostęp do zmiennego obiektu z oddzielnych wątków, musisz poradzić sobie z blokowaniem. Zmniejsza to przepustowość i znacznie utrudnia utrzymanie kodu . Wystarczająco skomplikowany system eliminuje ten problem tak daleko, że jego utrzymanie jest prawie niemożliwe (nawet dla ekspertów od współbieżności).
Niezmienne obiekty (a dokładniej niezmienne kolekcje) pozwalają uniknąć wszystkich tych problemów. Kiedy już zrozumiesz, jak działają, Twój kod rozwinie się w coś, co będzie łatwiejsze do odczytania, łatwiejsze w utrzymaniu i rzadziej zawiedzie w dziwny i nieprzewidywalny sposób. Niezmienne obiekty są jeszcze łatwiejsze do testowania, nie tylko ze względu na ich łatwą możliwość makietowania, ale także wzorce kodu, które zwykle narzucają. Krótko mówiąc, wszędzie są dobrą praktyką!
Powiedziawszy to, nie jestem fanatykiem w tej sprawie. Niektóre problemy po prostu nie wyglądają ładnie, gdy wszystko jest niezmienne. Ale myślę, że powinieneś spróbować popchnąć jak najwięcej swojego kodu w tym kierunku, zakładając oczywiście, że używasz języka, który sprawia, że jest to możliwa do utrzymania opinia (C / C ++ bardzo to utrudnia, podobnie jak Java) . Krótko mówiąc: korzyści zależą w pewnym stopniu od twojego problemu, ale wolałbym niezmienność.
źródło
Niezmienne obiekty a niezmienne kolekcje
Jednym z lepszych punktów w debacie na temat obiektów zmiennych i niezmiennych jest możliwość rozszerzenia pojęcia niezmienności na kolekcje. Niezmienny obiekt to obiekt, który często reprezentuje pojedynczą logiczną strukturę danych (na przykład niezmienny ciąg znaków). Gdy masz odwołanie do niezmiennego obiektu, zawartość obiektu nie ulegnie zmianie.
Niezmienna kolekcja to kolekcja, która nigdy się nie zmienia.
Kiedy wykonuję operację na mutowalnej kolekcji, zmieniam kolekcję w miejscu, a wszystkie jednostki, które mają odwołania do kolekcji, zobaczą zmianę.
Kiedy wykonuję operację na niezmiennej kolekcji, odwołanie jest zwracane do nowej kolekcji odzwierciedlającej zmianę. Wszystkie jednostki, które mają odniesienia do poprzednich wersji kolekcji, nie zobaczą zmiany.
Sprytne implementacje niekoniecznie muszą kopiować (klonować) całą kolekcję, aby zapewnić tę niezmienność. Najprostszym przykładem jest stos zaimplementowany jako pojedynczo połączona lista i operacje push / pop. Możesz ponownie wykorzystać wszystkie węzły z poprzedniej kolekcji w nowej kolekcji, dodając tylko jeden węzeł do wypychania i nie klonując żadnych węzłów do wyskakiwania. Z drugiej strony operacja push_tail na pojedynczo połączonej liście nie jest tak prosta ani wydajna.
Niezmienne a zmienne zmienne / odwołania
Niektóre języki funkcjonalne przyjmują koncepcję niezmienności do samych odniesień do obiektów, zezwalając tylko na przypisanie pojedynczego odniesienia.
Łatwość tworzenia a wydajność
Niemal zawsze powodem używania niezmiennego obiektu jest promowanie wolnego od efektów ubocznych programowania i prostego rozumowania kodu (szczególnie w środowisku wysoce współbieżnym / równoległym). Nie musisz się martwić, że dane źródłowe zostaną zmienione przez inną jednostkę, jeśli obiekt jest niezmienny.
Główną wadą jest wydajność. Oto opis prostego testu, który przeprowadziłem w Javie, porównując niektóre niezmienne i zmienne obiekty w problemie z zabawkami.
Problemy z wydajnością są dyskusyjne w wielu aplikacjach, ale nie we wszystkich, dlatego wiele dużych pakietów numerycznych, takich jak klasa Numpy Array w Pythonie, pozwala na aktualizacje w miejscu dużych tablic. Byłoby to ważne dla obszarów zastosowań, które wykorzystują duże operacje na macierzach i wektorach. Te duże, równoległe do danych i wymagające dużej mocy obliczeniowej problemy osiągają ogromne przyspieszenie dzięki działaniu w miejscu.
źródło
Sprawdź ten wpis na blogu: http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . Wyjaśnia, dlaczego niezmienne obiekty są lepsze niż zmienne. W skrócie:
źródło
Niezmienne obiekty to bardzo potężna koncepcja. Zdejmują wiele obowiązków związanych z utrzymaniem spójności obiektów / zmiennych dla wszystkich klientów.
Można ich używać do obiektów niskopoziomowych, niepolimorficznych - takich jak klasa CPoint - które są używane głównie z semantyką wartości.
Lub możesz ich użyć do wysokopoziomowych, polimorficznych interfejsów - takich jak IFunction reprezentujący funkcję matematyczną - która jest używana wyłącznie z semantyką obiektów.
Największa zaleta: niezmienność + semantyka obiektu + inteligentne wskaźniki sprawiają, że własność obiektu nie jest problemem, wszyscy klienci obiektu mają domyślnie własną prywatną kopię. W sposób dorozumiany oznacza to również deterministyczne zachowanie w obecności współbieżności.
Wada: gdy jest używany z obiektami zawierającymi dużo danych, zużycie pamięci może stać się problemem. Rozwiązaniem tego problemu mogłoby być zachowanie symboliki operacji na obiekcie i wykonanie leniwej oceny. Może to jednak prowadzić do łańcuchów obliczeń symbolicznych, które mogą negatywnie wpłynąć na wydajność, jeśli interfejs nie jest zaprojektowany do obsługi operacji symbolicznych. Coś, czego zdecydowanie należy unikać w tym przypadku, to zwracanie ogromnych fragmentów pamięci z metody. W połączeniu z połączonymi operacjami symbolicznymi może to prowadzić do ogromnego zużycia pamięci i obniżenia wydajności.
Tak więc niezmienne obiekty są zdecydowanie moim podstawowym sposobem myślenia o projektowaniu obiektowym, ale nie są dogmatem. Rozwiązują wiele problemów dla klientów obiektów, ale też tworzą wiele, szczególnie dla wykonawców.
źródło
Powinieneś określić, o jakim języku mówisz. W przypadku języków niskiego poziomu, takich jak C lub C ++, wolę używać obiektów mutowalnych, aby zaoszczędzić miejsce i zmniejszyć utratę pamięci. W językach wyższego poziomu niezmienne obiekty ułatwiają wnioskowanie o zachowaniu kodu (zwłaszcza kodu wielowątkowego), ponieważ nie ma „upiornej akcji na odległość”.
źródło
Zmienny obiekt to po prostu obiekt, który można zmodyfikować po utworzeniu / utworzeniu instancji, w porównaniu z niezmiennym obiektem, którego nie można zmodyfikować (zobacz stronę Wikipedii na ten temat). Przykładem tego w języku programowania są listy i krotki Pythona. Listy można modyfikować (np. Można dodawać nowe elementy po ich utworzeniu), podczas gdy krotki nie.
Nie sądzę, aby istniała jednoznaczna odpowiedź, która z nich jest lepsza w każdej sytuacji. Oboje mają swoje miejsca.
źródło
Jeśli typ klasy jest zmienny, zmienna tego typu klasy może mieć wiele różnych znaczeń. Na przykład załóżmy, że obiekt
foo
ma poleint[] arr
i zawiera odniesienie doint[3]
zbioru liczb {5, 7, 9}. Mimo że typ pola jest znany, istnieją co najmniej cztery różne rzeczy, które może reprezentować:Potencjalnie współdzielone odniesienie, którego posiadacze dbają tylko o to, aby hermetyzować wartości 5, 7 i 9. Jeśli
foo
chcearr
się hermetyzować różne wartości, musi zastąpić je inną tablicą zawierającą żądane wartości. Chcąc wykonać kopięfoo
, można podać kopię albo odniesienie do,arr
albo nową tablicę zawierającą wartości {1, 2, 3}, w zależności od tego, która z tych opcji jest wygodniejsza.Jedyne odniesienie, gdziekolwiek we wszechświecie, do tablicy zawierającej wartości 5, 7 i 9. zbiór trzech miejsc przechowywania, które w tej chwili przechowują wartości 5, 7 i 9; jeśli
foo
chce, aby hermetyzował wartości 5, 8 i 9, może albo zmienić drugą pozycję w tej tablicy, albo utworzyć nową tablicę zawierającą wartości 5, 8 i 9 i porzucić starą. Zauważ, że jeśli ktoś chciałby wykonać kopięfoo
, należy w kopii zastąpićarr
odwołaniem do nowej tablicy,foo.arr
aby pozostać jedynym odnośnikiem do tej tablicy w dowolnym miejscu we wszechświecie.Odniesienie do tablicy, której właścicielem jest inny obiekt, który z
foo
jakiegoś powodu ją wystawił (np. Być może chce tamfoo
przechowywać jakieś dane). W tym scenariuszuarr
nie hermetyzuje zawartości tablicy, ale raczej jej tożsamość . Ponieważ zastąpieniearr
odwołaniem do nowej tablicy całkowicie zmieniłoby jej znaczenie, kopiafoo
powinna zawierać odniesienie do tej samej tablicy.Odniesienie do tablicy, której
foo
jedynym właścicielem jest, ale do której z jakiegoś powodu odwołuje się inny obiekt (np. Chce, aby inny obiekt przechowywał tam dane - druga strona poprzedniego przypadku). W tym scenariuszuarr
hermetyzuje zarówno tożsamość tablicy, jak i jej zawartość. Zastąpieniearr
odwołaniem do nowej tablicy całkowicie zmieniłoby jej znaczenie, ale posiadaniearr
odwołania do klonafoo.arr
naruszyłoby założenie, żefoo
jest jedynym właścicielem. Nie ma więc możliwości kopiowaniafoo
.W teorii
int[]
powinien być ładnym, prostym, dobrze zdefiniowanym typem, ale ma cztery bardzo różne znaczenia. Natomiast odniesienie do niezmiennego obiektu (np.String
) Generalnie ma tylko jedno znaczenie. Duża część „mocy” niezmiennych obiektów wynika z tego faktu.źródło
Zmienne instancje są przekazywane przez odwołanie.
Niezmienne wystąpienia są przekazywane przez wartość.
Abstrakcyjny przykład. Załóżmy, że na moim dysku twardym istnieje plik o nazwie txtfile . Teraz, kiedy poprosisz mnie o txtfile , mogę go zwrócić w dwóch trybach:
W pierwszym trybie zwrócony plik txt jest plikiem modyfikowalnym , ponieważ kiedy robisz zmiany w pliku skrótu, robisz również zmiany w oryginalnym pliku. Zaletą tego trybu jest to, że każdy zwrócony skrót wymagał mniejszej ilości pamięci (na RAM lub na dysku twardym), a wadą jest to, że wszyscy (nie tylko ja, właściciel) mają uprawnienia do modyfikowania zawartości pliku.
W drugim trybie zwrócony plik txtfile jest niezmiennym plikiem, ponieważ wszystkie zmiany w otrzymanym pliku nie odnoszą się do oryginalnego pliku. Zaletą tego trybu jest to, że tylko ja (właściciel) mogę modyfikować oryginalny plik, a wadą jest to, że każda zwracana kopia wymaga pamięci (w RAM lub na HDD).
źródło
Jeśli zwrócisz odwołania do tablicy lub ciągu znaków, świat zewnętrzny może zmodyfikować zawartość tego obiektu, a tym samym uczynić go zmiennym (modyfikowalnym) obiektem.
źródło
Niezmiennych środków nie można zmienić, a zmienne oznaczają, że można się zmienić.
Obiekty różnią się od prymitywów w Javie. Prymitywy są wbudowane w typy (boolean, int itp.), A obiekty (klasy) są typami utworzonymi przez użytkownika.
Prymitywy i obiekty mogą być modyfikowalne lub niezmienne, jeśli są zdefiniowane jako zmienne składowe w ramach implementacji klasy.
Wiele osób uważa, że prymitywy i zmienne obiektowe mające przed sobą końcowy modyfikator są niezmienne, jednak nie jest to do końca prawdą. Więc finał prawie nie oznacza niezmienności dla zmiennych. Zobacz przykład tutaj
http://www.siteconsortium.com/h/D0000F.php .
źródło
Immutable object
- to stan obiektu, którego po utworzeniu nie można zmienić. Obiekt jest niezmienny, gdy wszystkie jego pola są niezmienneBezpieczny wątek
Główną zaletą obiektu Immutable jest to, że jest on naturalnym środowiskiem współbieżnym. Największym problemem we współbieżności jest
shared resource
to, że można zmienić dowolny wątek. Ale jeśli obiekt jest niezmienny, jestread-only
to operacja bezpieczna wątkowo. Każda modyfikacja oryginalnego niezmiennego obiektu zwraca kopięwolne od skutków ubocznych
Jako programista masz całkowitą pewność, że stanu niezmiennego obiektu nie można zmienić z dowolnego miejsca (celowo lub nie)
kompilacja optymalizacji
Polepszyć wydajność
Niekorzyść:
Kopiowanie obiektu jest operacją cięższą niż zmiana obiektu zmiennego, dlatego ma pewien wpływ na wydajność
Aby stworzyć
immutable
obiekt należy użyć:Poziom języka. Każdy język zawiera narzędzia, które Ci w tym pomogą. Na przykład Java ma
final
iprimitives
, Swift malet
istruct
[About] . Język definiuje typ zmiennej. Na przykład Java maprimitive
ireference
wpisz, Swift mavalue
ireference
wpisz [About] . Dla niezmiennych obiektów wygodniejsze jestprimitives
ivalue
wpisywanie, które domyślnie tworzy kopię. Jeśli chodzi oreference
typ, to jest to trudniejsze (ponieważ możesz z niego zmienić stan obiektu), ale możliwe. Na przykład możesz użyćclone
wzorca na poziomie programistyPoziom programisty. Jako programista nie powinieneś udostępniać interfejsu do zmiany stanu
źródło