Kod jak:
public Thing[] getThings(){
return things;
}
To nie ma większego sensu, ponieważ twoja metoda dostępu nie robi nic, tylko bezpośrednio zwraca wewnętrzną strukturę danych. Równie dobrze można po prostu zadeklarować Thing[] things
się public
. Ideą metody dostępu jest stworzenie interfejsu, który izoluje klientów od wewnętrznych zmian i uniemożliwia im manipulowanie rzeczywistą strukturą danych, z wyjątkiem dyskretnych sposobów, na jakie pozwala interfejs. Jak odkryłeś, kiedy cały kod klienta się zepsuł, twoja metoda dostępu tego nie zrobiła - to po prostu zmarnowany kod. Myślę, że wielu programistów ma tendencję do pisania takiego kodu, ponieważ dowiedzieli się gdzieś, że wszystko musi być otoczone metodami dostępu - ale to z powodów, które wyjaśniłem. Robienie tego tylko po to, by „podążać za formą”, gdy metoda dostępu nie służy żadnemu celowi, to po prostu hałas.
Zdecydowanie poleciłbym zaproponowane rozwiązanie, które realizuje niektóre z najważniejszych celów enkapsulacji: Zapewnienie klientom solidnego, dyskretnego interfejsu, który izoluje ich od wewnętrznych szczegółów implementacyjnych twojej klasy i nie pozwala im dotknąć wewnętrznej struktury danych oczekuj w sposób, który uznasz za odpowiedni - „prawo najmniej koniecznego przywileju”. Jeśli spojrzysz na duże popularne frameworki OOP, takie jak CLR, STL, VCL, zaproponowany przez ciebie wzorzec jest powszechny, właśnie z tego powodu.
Czy zawsze powinieneś to robić? Niekoniecznie. Na przykład, jeśli masz klasy pomocnika lub przyjaciela, które są zasadniczo składnikiem twojej głównej klasy robotniczej i nie są skierowane do przodu, nie jest to konieczne - to przesada, która doda wiele niepotrzebnego kodu. W takim przypadku w ogóle nie użyłbym metody dostępu - jak wyjaśniono - jest bez sensu. Po prostu zadeklaruj strukturę danych w sposób, który ogranicza się tylko do głównej klasy, która z niej korzysta - większość języków obsługuje takie sposoby - friend
lub deklarując ją w tym samym pliku, co główna klasa robotnicza itp.
Jedynym minusem, jaki widzę w twojej propozycji, jest to, że kodowanie wymaga więcej pracy (a teraz będziesz musiał ponownie kodować swoje klasy konsumenckie - ale i tak musiałeś / musiałeś to zrobić). Ale to nie jest tak naprawdę wada - musisz to zrobić dobrze, a czasem wymaga to więcej pracy.
Jedną z rzeczy, które sprawiają, że dobry programista jest dobry, jest to, że wiedzą, kiedy dodatkowa praca jest tego warta, a kiedy nie. W dłuższej perspektywie wprowadzenie dodatkowej kwoty w przyszłości przyniesie duże dywidendy - jeśli nie na tym projekcie, to na innych. Naucz się kodować we właściwy sposób i używaj do tego swojej głowy, a nie tylko automatycznie wykonuj określone formularze.
Należy pamiętać, że kolekcje bardziej złożone niż tablice mogą wymagać, aby klasa otaczająca zaimplementowała więcej niż trzy metody tylko w celu uzyskania dostępu do wewnętrznej struktury danych.
Jeśli ujawniasz całą strukturę danych za pośrednictwem zawierającej klasy, IMO musisz pomyśleć, dlaczego ta klasa jest w ogóle enkapsulowana, jeśli nie chodzi tylko o zapewnienie bezpieczniejszego interfejsu - „klasy opakowania”. Mówisz, że klasa zawierająca nie istnieje w tym celu - więc być może coś jest nie tak z twoim projektem. Zastanów się nad podziałem swoich klas na bardziej dyskretne moduły i warstwowaniem ich.
Klasa powinna mieć jeden jasny i dyskretny cel oraz zapewniać interfejs do obsługi tej funkcjonalności - nie więcej. Być może próbujesz połączyć ze sobą rzeczy, które do siebie nie pasują. Gdy to zrobisz, wszystko się zepsuje za każdym razem, gdy będziesz musiał wprowadzić zmianę. Im mniejsze i bardziej dyskretne są twoje zajęcia, tym łatwiej jest to zmieniać: Pomyśl LEGO.
get(index)
,add()
,size()
,remove(index)
, iremove(Object)
. Korzystając z zaproponowanej techniki, klasa zawierająca tę ArrayList musi mieć pięć publicznych metod tylko do delegowania do wewnętrznej kolekcji. A celem tej klasy w programie najprawdopodobniej nie jest hermetyzacja ArrayList, ale raczej zrobienie czegoś innego. ArrayList to tylko szczegół. [...]