Nie szukam opinii na temat semantyki, ale po prostu przypadek, w którym rozsądne użycie geterów jest prawdziwą przeszkodą. Może wpędza mnie to w niekończącą się spiralę polegania na nich, może alternatywą jest czystsze i automatycznie obsługuje getery itp. Coś konkretnego.
Słyszałem wszystkie argumenty, słyszałem, że są złe, ponieważ zmuszają cię do traktowania obiektów jako źródeł danych, że naruszają „czysty stan” obiektu „nie dawaj za dużo, ale bądź przygotowany na dużo zaakceptować ”.
Ale absolutnie nie ma żadnego rozsądnego powodu, dla którego a getData
jest złą rzeczą, w rzeczywistości kilka osób twierdziło, że chodzi o semantykę, gettery są per se per se, ale po prostu ich nie nazywaj getX
, dla mnie to jest co najmniej zabawne .
Czym jest jedna rzecz, bez opinii, która się zepsuje, jeśli będę korzystał z metod pobierających rozsądnie, a dla danych, które wyraźnie nie zepsują integralności obiektu, jeśli go wypuści?
Oczywiście, że zezwolenie na getter dla łańcucha, który jest używany do szyfrowania czegoś, jest bardziej głupi, ale mówię o danych, które twój system potrzebuje do działania. Być może twoje dane są wyciągane Provider
z obiektu, ale obiekt wciąż musi pozwalać na Provider
to $provider[$object]->getData
, nie da się tego obejść.
Dlaczego pytam: dla mnie getery, gdy są rozsądnie używane i na danych, które są traktowane jako „bezpieczne”, są wysyłane przez Boga, 99% moich geterów służy do identyfikacji obiektu, jak w, proszę, za pomocą kodu Object, what is your name? Object, what is your identifier?
, każdy, kto pracuje z obiektem, powinien znać te rzeczy na temat obiektu, ponieważ prawie wszystko w programowaniu to tożsamość, a kto inny wie lepiej, co to jest, niż sam obiekt? Więc nie widzę żadnych prawdziwych problemów, chyba że jesteś purystą.
Przejrzałem wszystkie pytania StackOverflow dotyczące „dlaczego osoby ustawiające / ustawiające” są złe i chociaż zgadzam się, że osoby ustawiające są naprawdę złe w 99% przypadków, nie trzeba traktować takich samych tylko dlatego, że rymują.
Seter naruszy tożsamość obiektu i bardzo utrudni debugowanie, kto zmienia dane, ale getter nic nie robi.
źródło
Odpowiedzi:
Nie możesz pisać dobrego kodu bez getterów.
Powodem tego nie jest to, że osoby pobierające nie przerywają enkapsulacji. Nie dzieje się tak dlatego, że osoby pobierające nie kuszą ludzi, aby nie zawracali sobie głowy obserwowaniem OOP, co pozwoliłoby im zastosować metody z danymi, na których działają. Oni robią. Nie, potrzebujesz getterów z powodu granic.
Pomysły enkapsulacji i utrzymywania metod wraz z danymi, na które działają, po prostu nie działają, gdy natrafisz na granicę, która powstrzymuje cię przed przeniesieniem metody i zmusza do przeniesienia danych.
To naprawdę takie proste. Jeśli użyjesz getterów, gdy nie ma granicy, w końcu nie będziesz mieć prawdziwych obiektów. Wszystko zaczyna mieć tendencję proceduralną. Który działa tak dobrze, jak kiedykolwiek.
True OOP nie jest czymś, co można rozpowszechniać wszędzie. Działa tylko w tych granicach.
Te granice nie są cienkie jak brzytwa. Mają w sobie kod. Ten kod nie może być OOP. To też nie może być funkcjonalne. Żaden kod nie pozbawił naszych ideałów, aby poradzić sobie z trudną rzeczywistością.
Michael Fetters nazwał ten kod powięź po tej białej tkance łącznej, która utrzymuje razem fragmenty pomarańczy.
To wspaniały sposób na przemyślenie tego. Wyjaśnia, dlaczego dobrze jest mieć oba rodzaje kodu w tej samej bazie kodu. Bez tej perspektywy wielu nowych programistów mocno trzyma się swoich ideałów, a następnie łamie im serca i rezygnuje z tych ideałów, gdy osiągną swoją pierwszą granicę.
Ideały działają tylko na swoim miejscu. Nie poddawaj się tylko dlatego, że nie wszędzie działają. Używaj ich tam, gdzie pracują. To miejsce jest soczystą częścią, którą chroni powięź.
Prostym przykładem granicy jest kolekcja. To coś trzyma i nie ma pojęcia, co to jest. Jak projektant kolekcji mógłby przenieść funkcjonalność behawioralną trzymanego obiektu do kolekcji, skoro nie ma pojęcia, co on będzie trzymał? Nie możesz Jesteś na granicy. Dlatego kolekcje mają gettery.
Teraz, jeśli wiesz, możesz zmienić to zachowanie i uniknąć zmiany stanu. Kiedy wiesz, powinieneś. Po prostu nie zawsze wiesz.
Niektórzy nazywają to po prostu pragmatycznym. I to jest. Ale miło wiedzieć, dlaczego musimy być pragmatyczni.
Wyraziłeś, że nie chcesz słyszeć argumentów semantycznych i wydaje się, że opowiadasz się za umieszczaniem wszędzie „rozsądnych pobudzających”. Pytasz o zakwestionowanie tego pomysłu. Myślę, że mogę pokazać, że pomysł ma problemy ze sposobem, w jaki go sformułowałeś. Ale wydaje mi się również, że wiem, skąd pochodzisz, ponieważ byłem tam.
Jeśli chcesz, aby użytkownicy pobierali wszędzie, spójrz na Python. Nie ma prywatnego słowa kluczowego. Jednak Python dobrze sobie radzi z OOP. W jaki sposób? Używają sztuczki semantycznej. Wymieniają wszystko, co ma być prywatne, z wiodącym podkreśleniem. Możesz nawet z niego czytać, pod warunkiem że bierzesz za to odpowiedzialność. „Wszyscy tu jesteśmy dorośli”, często mówią.
Jaka jest różnica między tym a umieszczaniem programów pobierających wszystko na Java lub C #? Przepraszam, ale to semantyka. Konwencja podkreślania w Pythonie wyraźnie sygnalizuje, że grzebiesz za drzwiami tylko dla pracowników. Uderz we wszystko, a stracisz ten sygnał. Dzięki refleksji mogłeś i tak usunąć prywatność i nadal nie stracić sygnału semantycznego. Po prostu nie ma tu argumentu strukturalnego.
Pozostaje nam więc zadecydować, gdzie zawiesić znak „tylko dla pracowników”. Co należy uznać za prywatne? Nazywacie to „rozsądnymi pobieraczami”. Jak powiedziałem, najlepszym uzasadnieniem dla getterów jest granica, która zmusza nas do oddalenia się od naszych ideałów. Nie powinno to skutkować uzyskaniem wszystkiego. Kiedy to spowoduje getter, powinieneś rozważyć przeniesienie zachowania dalej do soczystego kawałka, gdzie możesz je chronić.
Ten rozdział doprowadził do kilku warunków. Obiekt transferu danych lub DTO nie zachowuje się. Jedynymi metodami są pobierające, a czasem ustawiające, czasem konstruktor. Ta nazwa jest niefortunna, ponieważ wcale nie jest prawdziwym przedmiotem. Programy pobierające i ustawiające są tak naprawdę tylko kodem debugującym, który daje miejsce do ustawienia punktu przerwania. Gdyby nie ta potrzeba, byliby tylko stosem publicznych pól. W C ++ nazywaliśmy je strukturami. Jedyną różnicą, jaką mieli od klasy C ++, było to, że domyślnie nie ustawili na public.
DTO są fajne, ponieważ można je przerzucić przez ścianę graniczną i bezpiecznie przechowywać inne metody w ładnym, soczystym obiekcie zachowania. Prawdziwy przedmiot. Bez narzędzi pobudzających do naruszenia jest hermetyzacja. Moje obiekty zachowania mogą jeść DTO, używając ich jako obiektów parametrów . Czasami muszę wykonać jego obronną kopię , aby zapobiec wspólnemu, zmiennemu stanowi . Nie rozprowadzam modyfikowalnych DTO wewnątrz soczystej części w obrębie granicy. Zamykam je. Chowam je. A kiedy w końcu wpadam na nową granicę, odkrywam nowe DTO i rzucam je na ścianę, sprawiając, że jest to problem kogoś innego.
Ale chcesz zapewnić użytkownikom, którzy wyrażają tożsamość. Gratulacje, znalazłeś granicę. Podmioty mają tożsamość, która wykracza poza ich odniesienie. Oznacza to, że poza ich adresem pamięci. Więc to musi być gdzieś przechowywane. I coś musi być w stanie odnosić się do tego poprzez swoją tożsamość. Getter, który wyraża tożsamość, jest całkowicie rozsądny. Stos kodu, który używa tego modułu pobierającego do podejmowania decyzji, które jednostka mogła sama podjąć, nie jest.
W końcu to nie istnienie pobierających jest błędne. Są znacznie lepsze niż pola publiczne. Złe jest to, że są one używane do udawania, że jesteś zorientowany obiektowo, kiedy nie jesteś. Getters są dobrzy. Bycie zorientowanym obiektowo jest dobre. Gettery nie są zorientowane obiektowo. Użyj narzędzi pobierających, aby wykroić bezpieczne miejsce, w którym będzie zorientowany obiektowo.
źródło
clearly
? Jeśli dane są mi przekazywane z czegoś, pod względem wartości , oczywiście, mój obiekt jest teraz właścicielem danych, ale według semantyki, w tym momencie jeszcze nie otrzymuje danych od kogoś innego. Następnie musi wykonać operacje, aby przekształcić te dane, czyniąc je własnymi, w momencie dotknięcia danych, musisz wprowadzić zmiany semantyczne: Masz dane o nazwie jak$data_from_request
i teraz operujesz na nich? Nazwij to$changed_data
. Chcesz udostępnić te dane innym osobom? Utwórz gettergetChangedRequestData
, więc własność jest wyraźnie ustawiona. Albo to jest?Getters naruszają zasadę Hollywood („Nie dzwoń do nas, zadzwonimy”)
Zasada hollywoodzka (inaczej Inversion of Control) stwierdza, że nie wywołujesz kodu biblioteki, aby załatwić sprawę; raczej framework wywołuje twój kod. Ponieważ środowisko kontroluje rzeczy, rozgłaszanie stanu wewnętrznego klientom nie jest konieczne. Nie musisz tego wiedzieć.
W najbardziej podstępnej formie, naruszenie zasady hollywoodzkiej oznacza, że używasz narzędzia pobierającego, aby uzyskać informacje o stanie klasy, a następnie podejmujesz decyzje, które metody wywołać tę klasę na podstawie uzyskanej wartości. jest to naruszenie enkapsulacji w najlepszym wydaniu.
Korzystanie z narzędzia pobierającego oznacza, że potrzebujesz tej wartości, a tak naprawdę nie.
Być może rzeczywiście potrzebujesz poprawy wydajności
W skrajnych przypadkach lekkich przedmiotów, które muszą mieć maksymalną możliwą wydajność, możliwe (choć wyjątkowo mało prawdopodobne) jest to, że nie możesz zapłacić bardzo małej kary za wydajność, którą narzuca getter. Nie wydarzy się to 99,9 procent czasu.
źródło
Generator
obiekt, który przechodzi przez wszystkie mojeItems
obiekty, a następnie wywołujegetName
od każdego z nich,Item
aby zrobić coś dalej. Na czym polega problem? Następnie w zamianGenerator
wyrzuca sformatowane ciągi. Jest to w mojej strukturze, dla której mam interfejs API, z którego ludzie mogą korzystać, aby uruchamiać wszystko, co zapewniają użytkownicy, ale bez dotykania struktury.What is the issue with this?
Brak, który widzę. Zasadniczo tomap
robi funkcja. Ale to nie pytanie, które zadałeś. Zasadniczo zapytałeś: „Czy są jakieś warunki, w których osoba pobierająca może być niewskazana”. Odpowiedziałem dwoma, ale to nie znaczy, że całkowicie porzuciłeś seterów.Getters Leak Szczegóły implementacji i Break Abstrakcji
Rozważać
public int millisecondsSince1970()
Twoi klienci pomyślą: „och, to jest int” i będą dzwonili gazilliony, zakładając, że jest to int , robi liczby całkowite, porównując daty itp. Kiedy zdasz sobie sprawę, że potrzebujesz długiej , będzie dużo starszego kodu z problemami. W świecie Java dodajesz
@deprecated
interfejs API, wszyscy go zignorują i utkniesz w utrzymywaniu przestarzałego, błędnego kodu. :-( (Zakładam, że inne języki są podobne!)W tym konkretnym przypadku nie jestem pewien, jaka może być lepsza opcja, a odpowiedź @candiedorange jest całkowicie trafna, wiele razy potrzebujesz getterów, ale ten przykład ilustruje wady. Każdy publiczny getter ma tendencję do „blokowania” cię w określonej implementacji. Używaj ich, jeśli naprawdę potrzebujesz „przekraczać granice”, ale używaj tak mało, jak to możliwe, ostrożnie i rozważnie.
źródło
timepoint
itimespan
odpowiednio. (epoch
jest szczególną wartościątimepoint
.) W tych klasach udostępniają funkcje pobierające takie jakgetInt
lubgetLong
lubgetDouble
. Jeśli klasy, które manipulują czasem, są zapisywane w taki sposób, że (1) delegują arytmetykę związaną z czasem do tych obiektów i metod oraz (2) zapewniają dostęp do tych obiektów czasu za pośrednictwem modułów pobierających, wówczas problem zostanie rozwiązany bez konieczności pozbycia się funkcji pobierających .Myślę, że pierwszym kluczem jest pamiętać, że absoluty są zawsze w błędzie.
Zastanów się nad programem książki adresowej, który po prostu przechowuje dane kontaktowe osób. Ale zróbmy to dobrze i podzielmy na warstwy kontrolera / usługi / repozytorium.
Będzie
Person
klasa przechowująca rzeczywiste dane: imię i nazwisko, adres, numer telefonu itp.,Address
A takżePhone
klasy, dzięki czemu będziemy mogli później wykorzystać polimorfizm do obsługi programów spoza USA. Wszystkie trzy z tych klas są obiektami do przesyłania danych , więc nie będą niczym innym jak obiektami pobierającymi i ustawiającymi. I to ma sens: nie będą mieli w sobie żadnej logiki poza nadpisywaniemequals
igetHashcode
; poproszenie ich o przedstawienie informacji pełnej osoby nie ma sensu: czy jest to prezentacja HTML, niestandardowa aplikacja GUI, aplikacja konsoli, punkt końcowy HTTP itp.?Tam właśnie wkracza kontroler, usługa i repozytorium. Wszystkie te klasy będą obciążone logiką i łatwiejsze, dzięki wstrzykiwaniu zależności.
Do kontrolera zostanie wstrzyknięta usługa, która ujawni metody takie jak
get
isave
, które przyjmą pewne uzasadnione argumenty (np.get
Mogą mieć przesłonięcia do wyszukiwania według nazwy, wyszukiwania według kodu pocztowego lub po prostu uzyskać wszystko;save
prawdopodobnie zajmie jedenPerson
i zapisz go). Jakie funkcje pobierania miałby kontroler? Możliwość uzyskania nazwy konkretnej klasy może być przydatna do rejestrowania, ale w jakim stanie będzie ona inna niż stan, w który została wstrzyknięta?Podobnie warstwa usługowa zostanie wstrzyknięta do repozytorium, dzięki czemu może faktycznie uzyskać i zachować
Person
s, i może mieć pewną „logikę biznesową” (np. Może będziemy wymagać, aby wszystkiePerson
obiekty miały co najmniej jeden adres lub telefon numer). Ale znowu: w jakim stanie znajduje się ten obiekt poza tym, co zostało wstrzyknięte? Znowu żaden.Warstwa repozytorium jest tam, gdzie robi się trochę bardziej interesująco: ustanowi połączenie z miejscem przechowywania, np. plik, baza danych SQL lub magazyn w pamięci. Kusi tutaj, aby dodać moduł pobierający, aby uzyskać nazwę pliku lub ciąg połączenia SQL, ale taki stan został wprowadzony do klasy. Czy repozytorium powinno mieć moduł pobierający informacje, czy pomyślnie nawiązał połączenie ze swoim magazynem danych? Cóż, może, ale jaki pożytek ma ta informacja poza samą klasą repozytorium? Sama klasa repozytorium powinna być w stanie podjąć próbę ponownego połączenia się ze swoim magazynem danych, jeśli zajdzie taka potrzeba, co sugeruje, że
isConnected
właściwość ma już wątpliwą wartość. Co więcej,isConnected
właściwość prawdopodobnie będzie błędna tylko wtedy, gdy będzie najbardziej potrzebna: sprawdzanieisConnected
przed próbą pobrania / przechowywania danych nie gwarantuje, że repozytorium będzie nadal połączone, gdy zostanie wykonane „prawdziwe” wywołanie, więc nie eliminuje potrzeby gdzieś obsługi wyjątków (istnieją argumenty za tym, gdzie to powinno pójść, poza zakres tego pytania).Zastanów się także nad testami jednostkowymi (piszesz testy jednostkowe, prawda?): Usługa nie będzie oczekiwać wstrzyknięcia konkretnej klasy, a raczej oczekiwać, że zostanie wstrzyknięta konkretna implementacja interfejsu. Pozwala to aplikacji jako całości zmienić miejsce przechowywania danych bez konieczności robienia czegokolwiek poza wymianą wstrzykiwanego repozytorium do usługi. Umożliwia także testowanie usługi przez wstrzyknięcie fałszywego repozytorium. Oznacza to myślenie w kategoriach interfejsów, a nie klas. Czy interfejs repozytorium ujawniłby jakieś moduły pobierające? To znaczy, czy jest jakiś stan wewnętrzny, który byłby: wspólny dla wszystkich repozytoriów, użyteczny poza repozytorium i nie wstrzykiwany do repozytorium? Trudno byłoby mi o tym myśleć.
TL; DR
Podsumowując: oprócz klas, których jedynym celem jest przenoszenie danych, nic innego na stosie nie ma miejsca na umieszczenie gettera: stan jest wstrzykiwany lub nieistotny poza klasą robotniczą.
źródło
Zmienni członkowie.
Jeśli masz kolekcję rzeczy w obiekcie, który wystawiasz za pośrednictwem modułu pobierającego, potencjalnie narażasz się na błędy związane z dodawaniem niewłaściwych rzeczy do kolekcji.
Jeśli masz zmienny obiekt, który odsłaniasz za pomocą modułu pobierającego, potencjalnie otwierasz się na wewnętrzny stan swojego obiektu zmieniany w sposób, którego się nie spodziewasz.
Naprawdę złym przykładem byłby obiekt, który liczy od 1 do 100, ujawniając jego bieżącą wartość jako zmienne odwołanie. Pozwala to obiektowi trzeciemu zmienić wartość prądu w taki sposób, że wartość może znajdować się poza oczekiwanymi granicami.
Jest to głównie problem z odsłonięciem zmiennych obiektów. Odsłonięcie struktur lub niezmiennych obiektów wcale nie stanowi problemu, ponieważ wszelkie zmiany w nich spowodują zmianę kopii (zwykle albo przez niejawną kopię w przypadku struktury lub przez wyraźną kopię w przypadku obiektu niezmiennego).
Aby użyć metafory, która może ułatwić dostrzeżenie różnicy.
Kwiat ma wiele unikalnych cech. Ma
Color
i ma wiele płatków (NumPetals). Jeśli obserwuję ten kwiat, w prawdziwym świecie wyraźnie widzę jego kolor. Następnie mogę podejmować decyzje na podstawie tego koloru. Na przykład jeśli kolor jest czarny, nie dawaj dziewczynie, ale jeśli kolor jest czerwony, dawaj dziewczynie. W naszym modelu obiektowym kolor kwiatu byłby odsłonięty jako getter na obiekcie kwiatowym. Moja obserwacja tego koloru jest ważna dla działań, które wykonam, ale nie ma żadnego wpływu na obiekt kwiatowy. Nie powinienem być w stanie zmienić koloru tego kwiatu. Podobnie ukrywanie właściwości koloru nie ma sensu. Kwiat ogólnie nie może powstrzymywać ludzi przed obserwowaniem jego koloru.Jeśli farbuję kwiat, powinienem wywołać na kwiatku metodę ReactToDye (Color dyeColor), która zmieni kwiat zgodnie z jego wewnętrznymi regułami. Następnie mogę ponownie zapytać o
Color
właściwość i zareagować na każdą zmianę w niej po wywołaniu metody ReactToDye. Błędem byłoby dla mnie bezpośrednie modyfikowanieColor
kwiatu, a gdybym mógł, abstrakcja się załamała.Czasami (rzadziej, ale wciąż wystarczająco często, że warto wspomnieć) setery są dość poprawnym projektem OO. Jeśli mam obiekt klienta, uzasadnione jest ujawnienie obiektu ustawiającego jego adres. Czy to się nazywa,
setAddress(string address)
czyChangeMyAddressBecauseIMoved(string newAddress)
po prostustring Address { get; set; }
jest kwestią semantyki. Stan wewnętrzny tego obiektu musi się zmienić, a właściwym sposobem na to jest ustawienie stanu wewnętrznego tego obiektu. Nawet jeśli potrzebuję historycznego zapisu adresów, wCustomer
których mieszkałem, mogę użyć setera, aby odpowiednio zmienić mój stan wewnętrzny. W tym przypadku nie ma sensu,Customer
aby były niezmienne i nie ma lepszego sposobu na zmianę adresu niż zapewnienie setterowi, aby to zrobił.Nie jestem pewien, kto sugeruje, że Gettery i setery są złe lub psują paradygmaty obiektowe, ale jeśli tak, prawdopodobnie robią to w odpowiedzi na określoną funkcję językową języka, którego używają. Mam wrażenie, że wyrosło to z kultury Java „wszystko jest przedmiotem” (i prawdopodobnie rozszerzyło się na inne języki). Świat .NET w ogóle nie ma tej dyskusji. Getters and Setters to pierwszorzędna funkcja językowa, z której korzystają nie tylko osoby piszące aplikacje w tym języku, ale także sam interfejs API języka.
Należy zachować ostrożność podczas korzystania z programów pobierających. Nie chcesz narażać niewłaściwych danych i nie chcesz narażać danych w niewłaściwy sposób (tj. Obiekty zmienne, odniesienia do struktur, które mogą pozwolić konsumentowi modyfikować stan wewnętrzny). Ale chcesz modelować swoje obiekty, aby dane były kapsułkowane w taki sposób, aby Twoje obiekty mogły być używane przez zewnętrznych konsumentów i chronione przed niewłaściwym użyciem przez tych samych konsumentów. Często będzie to wymagać osób pobierających.
Podsumowując: Gettery i Settery są ważnymi obiektowymi narzędziami do projektowania. Nie należy ich używać tak swobodnie, że każdy szczegół implementacji jest ujawniany, ale nie należy ich używać tak oszczędnie, aby konsumenci obiektu nie mogli efektywnie z niego korzystać.
źródło
Utrzymanie zostanie zerwane.
Za każdym razem (powinienem powiedzieć prawie zawsze) oferujesz łapacz, który w zasadzie oddajesz więcej niż o to prosiłeś. Oznacza to również, że musisz utrzymać więcej, niż zostało to poproszone, a więc obniżyć łatwość konserwacji bez prawdziwego powodu.
Przejdźmy do
Collection
przykładu @ candied_orange . W przeciwieństwie do tego, co pisze,Collection
nie powinien mieć gettera. Kolekcje istnieją z bardzo szczególnych powodów, w szczególności w celu iteracji wszystkich przedmiotów. Ta funkcja nie jest dla nikogo zaskoczeniem, dlatego powinna zostać zaimplementowana wCollection
, zamiast narzucać ten oczywisty przypadek użycia użytkownikowi, używając cykli i getterów itp.Aby być sprawiedliwym, niektóre granice zrobić pobierające potrzebie. Dzieje się tak, jeśli naprawdę nie wiesz, do czego będą używane. Na przykład obecnie można programowo uzyskać ślad stosu z
Exception
klasy Javy , ponieważ ludzie chcieli go używać z różnych ciekawych powodów, których pisarze nie wyobrażali sobie lub nie mogli przewidzieć.źródło
Collection
s jednocześnie, jak w(A1, B1), (A2, B2), ...
. Wymaga to wdrożenia „zip”. Jak zaimplementować „zip”, jeśli na jednym z nich zaimplementowano funkcję iteracjiCollection
- jak uzyskać dostęp do odpowiedniego elementu w drugim?Collection
Musi implementowaćzip
się sam, w zależności od tego, jak biblioteka jest napisana. Jeśli nie, zaimplementuj go i prześlij do twórcy biblioteki żądanie ściągnięcia. Zauważ, że zgodziłem się, że niektóre granice czasami wymagają getterów, moim prawdziwym problemem jest to, że getters są obecnie wszędzie.Collection
obiekcie, przynajmniej dla własnej implementacjizip
. (Oznacza to, że program pobierający musi mieć przynajmniej widoczność pakietu.) A terazCollection
Oczywiście mogą uzyskać dostęp do własnego stanu wewnętrznego, lub dokładniej każdyCollection
przypadek można uzyskać dostępu do innych na stan wewnętrzny. Jest tego samego typu, nie wymaga pobierania.