Jest to kluczowa kwestia, ale IMHO jest warte zrozumienia.
Wszystkie języki OO zawsze wykonują kopie referencji i nigdy nie kopiują obiektu „niewidocznie”. O wiele trudniej byłoby pisać programy, gdyby języki OO działały w jakikolwiek inny sposób. Na przykład funkcje i metody nigdy nie mogłyby zaktualizować obiektu. Java i większość języków OO byłyby prawie niemożliwe do użycia bez znacznej dodatkowej złożoności.
Obiekt w programie powinien mieć jakieś znaczenie. Na przykład reprezentuje coś konkretnego w prawdziwym świecie fizycznym. Zwykle wiele odniesień do tej samej rzeczy ma sens. Na przykład mój adres domowy można podać wielu osobom i organizacjom, a adres ten zawsze odnosi się do tej samej fizycznej lokalizacji. Pierwszą kwestią jest to, że obiekty często przedstawiają coś konkretnego, rzeczywistego lub konkretnego; dlatego posiadanie wielu odniesień do tej samej rzeczy jest niezwykle przydatne. W przeciwnym razie trudniej byłoby pisać programy.
Za każdym razem, a
gdy przekazujesz jako argument / parametr do innej funkcji, np. Wywołania
foo(Dog aDoggy);
lub zastosowania metody a
, bazowy kod programu tworzy kopię odwołania, aby utworzyć drugie odwołanie do tego samego obiektu.
Ponadto, jeśli kod z skopiowanym odwołaniem znajduje się w innym wątku, oba mogą być używane jednocześnie, aby uzyskać dostęp do tego samego obiektu.
Tak więc w najbardziej przydatnych programach będzie wiele odniesień do tego samego obiektu, ponieważ jest to semantyka większości języków programowania OO.
Teraz, jeśli się nad tym zastanowimy, ponieważ przekazywanie przez referencje jest jedynym mechanizmem dostępnym w wielu językach OO (C ++ obsługuje oba), możemy oczekiwać, że będzie to „prawidłowe” zachowanie domyślne .
IMHO, używanie referencji jest właściwym ustawieniem domyślnym z kilku powodów:
- Gwarantuje to, że wartość obiektu użytego w dwóch różnych miejscach jest taka sama. Wyobraź sobie umieszczenie obiektu w dwóch różnych strukturach danych (tablice, listy itp.) I wykonanie pewnych operacji na obiekcie, który go zmienia. Debugowanie może być koszmarem. Co ważniejsze, jest to ten sam obiekt w obu strukturach danych lub w programie występuje błąd.
- Możesz szczęśliwie zmienić kod na kilka funkcji lub scalić kod z kilku funkcji w jedną, a semantyka się nie zmienia. Gdyby język nie zapewniał semantyki odniesienia, modyfikacja kodu byłaby jeszcze bardziej złożona.
Istnieje również argument dotyczący wydajności; wykonywanie kopii całych obiektów jest mniej wydajne niż kopiowanie odwołania. Myślę jednak, że to nie ma sensu. Liczne odniesienia do tego samego obiektu mają większy sens i są łatwiejsze w użyciu, ponieważ pasują do semantyki prawdziwego świata fizycznego.
Tak więc, IMHO, zwykle ma sens wiele odniesień do tego samego obiektu. W nietypowych przypadkach, gdy nie ma to sensu w kontekście algorytmu, większość języków zapewnia możliwość „klonowania” lub głębokiej kopii. Nie jest to jednak domyślne.
Myślę, że ludzie, którzy twierdzą, że to nie powinno być domyślne, używają języka, który nie zapewnia automatycznego usuwania śmieci. Na przykład staroświecki C ++. Problem polega na tym, że muszą znaleźć sposób na zbieranie „martwych” przedmiotów, a nie na odzyskiwanie przedmiotów, które mogą być nadal potrzebne; posiadanie wielu odniesień do tego samego obiektu sprawia, że jest to trudne.
Myślę, że jeśli C ++ miał wystarczająco tanie wyrzucanie elementów bezużytecznych, aby wszystkie obiekty, do których istnieją odniesienia, były usuwane, to znaczna część sprzeciwu zniknęła. Nadal będą przypadki, w których semantyka odniesienia nie jest potrzebna. Jednak z mojego doświadczenia wynika, że ludzie, którzy potrafią zidentyfikować te sytuacje, zazwyczaj są w stanie wybrać odpowiednią semantykę.
Uważam, że istnieją pewne dowody na to, że duża część kodu w programie C ++ służy do obsługi lub łagodzenia usuwania śmieci. Jednak pisanie i utrzymywanie tego rodzaju „infrastrukturalnego” kodu zwiększa koszty; ma na celu ułatwienie korzystania z języka lub zwiększenie jego niezawodności. Na przykład język Go został zaprojektowany z naciskiem na usunięcie niektórych słabości C ++ i nie ma innego wyboru niż wyrzucanie elementów bezużytecznych.
Jest to oczywiście nieistotne w kontekście Java. To również zostało zaprojektowane tak, aby było łatwe w użyciu, podobnie jak zbieranie śmieci. Dlatego posiadanie wielu odniesień jest domyślną semantyką i jest względnie bezpieczne w tym sensie, że obiekty nie są odzyskiwane, gdy istnieje odniesienie do nich. Oczywiście mogą być trzymane przez strukturę danych, ponieważ program nie porządkuje właściwie, gdy naprawdę zakończy się na obiekcie.
Powracając do pytania (z pewnym uogólnieniem), kiedy chciałbyś mieć więcej niż jedno odniesienie do tego samego obiektu? Prawie w każdej sytuacji, o której mogę pomyśleć. Są one domyślną semantyką większości mechanizmów przekazywania parametrów języków. Sugeruję, że dzieje się tak, ponieważ domyślna semantyka obsługi obiektów, która istnieje w realnym świecie, musi być w zasadzie przez odniesienie (ponieważ rzeczywiste obiekty są tam).
Każda inna semantyka byłaby trudniejsza do opanowania.
Dog a = new Dog("rover"); // initialise with name
DogList dl = new DogList()
dl.add(a)
...
a.setOwner("Mr Been")
Sugeruję, że „łazik” dl
powinien być tym, na który wpływ mają setOwner
lub programy będą miały trudności z pisaniem, zrozumieniem, debugowaniem lub modyfikacją. Myślę, że większość programistów byłaby zdziwiona lub przerażona inaczej.
później pies jest sprzedawany:
soldDog = dl.lookupOwner("rover", "Mr Been")
soldDog.setOwner("Mr Mcgoo")
Ten rodzaj przetwarzania jest powszechny i normalny. Tak więc semantyka odniesienia jest domyślna, ponieważ zazwyczaj ma ona największy sens.
Podsumowanie: Zawsze warto mieć wiele odwołań do tego samego obiektu.