Jaki jest konkretny problem z dopuszczaniem osób pobierających?


15

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 getDatajest 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 Providerz obiektu, ale obiekt wciąż musi pozwalać na Providerto $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.


2
Radzę zapomnieć o większości teorii OO. Ćwiczyć. Napisz dużo kodu, a następnie go zachowaj. Dowiesz się o wiele więcej o tym, co działa dobrze, a co nie, od zrobienia czegoś, a następnie powrotu do niego kilka miesięcy później.
jpmc26

1
@ jpmc26 Co u matki .... Nigdy tak naprawdę nie zdawałem sobie sprawy, że nikt z nas nie robi właściwie OOP, zawsze miałem pojęcie enkapsulacji ściśle związane z polami obiektu, ale sam obiekt jest stanem i jest swobodnie udostępniane. Naprawdę każdy źle robi OOP, a raczej OOP nie jest możliwe, jeśli paradygmat jest przedmiotem. Używam OOP ściśle, aby wyrazić, jak działa system, i nigdy nie traktowałem go jako świętego Graala, ani SOLIDNEGO. To tylko narzędzia, których używam głównie do zdefiniowania rozsądnie dobrze wykorzystanej (poprzez wdrożenie) mojej koncepcji. Ktoś czyta dalej, jeśli to prawda?
coolpasta

2
--cont, Im więcej piszę, tym bardziej zdaję sobie sprawę z tego, że programowanie polega na zdolności do abstrakcji tyle, ile jest to konieczne, aby móc rozumieć, dla innych, a także dla ciebie na temat twojej bazy kodu. Oczywiście techniczną stroną tego jest upewnienie się, że wykonujesz odpowiednie kontrole / ustanawiasz właściwe reguły. W rzeczywistości ten, kto zdoła sprawić, by inni zrozumieli swój kod, wygrywa. Upewnij się, że ma to sens dla każdego, kto go czyta, a następnie upewnij się, że nie zawiedzie, odpowiednio segmentując obiekty za pomocą interfejsów, sprawdzając i przełączając się na inne paradygmaty, gdy łatwiej jest z nimi skorzystać: bądź elastyczny.
coolpasta

2
Dla mnie to wygląda na słomkę. Nie sądzę, aby jakikolwiek poważny programista, który faktycznie pisze użyteczny kod, argumentowałby, że rozsądne używanie geterów jest problemem. Pytanie sugeruje, że osoby pobierające mogą być używane rozsądnie, co dodatkowo oznacza, że ​​osoby pobierające mogą być rozsądne. Szukasz kogoś, kto powiedziałby, że nie ma sensownego użycia getterów? Poczekasz chwilę.
JimmyJames

2
@coolpasta Masz rację. Nie obchodzi mnie to, czy to czysty OOP. Tylko lepiej. Daj mi wybór między zepsutym kodem, który może być zrozumiany przez ludzi, a bezbłędnym kodem kochającym procesor, który jest nieczytelny, i za każdym razem przyjmuję przyjazny dla człowieka kod. OOP przydaje mi się TYLKO wtedy, gdy zmniejsza liczbę rzeczy, o których muszę pomyśleć w danym momencie. To jedyny powód, dla którego podobają mi się proste procedury. Bez tego zmuszasz mnie do przeskakiwania przez wiele plików kodu źródłowego bez powodu.
candied_orange

Odpowiedzi:


22

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.


Właśnie dlatego zapytałem i nie jestem pewien, czy moja rozmowa jest widoczna z Robertem, starałem się również znaleźć konkretny przypadek, w którym wyraźnie boli mnie używanie „rozsądnych pobudzaczy”. Ja sam nie zgadzam się z seterami, ponieważ przynajmniej dla mnie bardzo trudno przypisać własność takiej akcji, używałem seterów we wszystkim i zanim zdałem sobie sprawę, że nawet 1 seter, na który wpływa zbyt wiele obiektów, zabija ja ... Musiałem przepisać wszystko. Rozsądne stosowanie getterów po tak długich wyczerpujących testach, poważnie nie ma wad, jeśli nie jesteś purystą.
coolpasta

1
@coolpasta możesz tworzyć obiekty właściciela danych. Obiekty właściciela danych są obiektami danych, ale zostały zaprojektowane i nazwane tak, aby (wyraźnie) były właścicielami danych. Oczywiście te obiekty będą miały gettery.
rwong

1
@rwong Co masz na myśli 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_requesti teraz operujesz na nich? Nazwij to $changed_data. Chcesz udostępnić te dane innym osobom? Utwórz getter getChangedRequestData, więc własność jest wyraźnie ustawiona. Albo to jest?
coolpasta

1
„Nie można pisać dobrego kodu bez programów pobierających.” Jest to całkowicie błędne, podobnie jak przekonanie, że granice z natury wymagają osób pobierających. To nie jest pragmatyzm, tylko lenistwo. To jest trochę łatwiej po prostu wyrzucić danych przez płot i być z nim zrobić, myśląc o sprawach użytkowych i projektowania dobry api behawioralna jest trudne.
Robert Bräutigam

2
Doskonała odpowiedź!
cmaster

10

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.


1
Rozumiem i polecę z twoją prawdą, że nie muszę wiedzieć. . Ale dotarłem do miejsca, w którym muszę. Mam Generatorobiekt, który przechodzi przez wszystkie moje Itemsobiekty, a następnie wywołuje getNameod każdego z nich, Itemaby zrobić coś dalej. Na czym polega problem? Następnie w zamian Generatorwyrzuca 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.
coolpasta

3
What is the issue with this?Brak, który widzę. Zasadniczo to maprobi 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.
Robert Harvey

1
Zadajesz niewłaściwemu facetowi to pytanie. Jestem pragmatykiem; Robię wszystko, co najlepiej pasuje do moich konkretnych programów i nie wkładam zbyt wiele w „zasady”, chyba że służą moim celom.
Robert Harvey

2
@ jpmc26: Frameworks. Nie biblioteki.
Robert Harvey

2
Framework to próba przekształcenia języka ogólnego przeznaczenia w język specyficzny dla domeny. Biblioteka to zbiór algorytmów wielokrotnego użytku. Każdy z nich poprosi cię o uzależnienie. To od Ciebie zależy, czy chcesz na stałe ustalić zależność kodu lub sprawić, by zależność była łatwo zastępowalna.
candied_orange

2

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 @deprecatedinterfejs 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.


To prawda, ale jakie jest rozwiązanie wzorcowe do wymuszania typu danych / struktury do zmiennej obiektu, a także do wszystkiego, co używa gettera dla tej zmiennej?
coolpasta

1
Ten przykład ilustruje inne zjawisko, prymitywną obsesję. Obecnie większość bibliotek i ram ma klasy, które reprezentują timepointi timespanodpowiednio. ( epochjest szczególną wartością timepoint.) W tych klasach udostępniają funkcje pobierające takie jak getIntlub getLonglub getDouble. 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 .
rwong

1
Podsumowując, funkcje pobierające są częścią interfejsu API, dlatego też funkcje pobierające muszą być zaprojektowane z wystarczającym uwzględnieniem wszystkich konsekwencji, w tym przeszłych, obecnych i przyszłych. Ale to nie prowadzi do wniosku o uniknięciu lub zminimalizowaniu liczby pobierających. W rzeczywistości, jeśli istnieje potrzeba uzyskania dostępu do określonej informacji, dla której pominięto getter, wymagałoby to zmiany interfejsu API po stronie biblioteki i zmiany kodu. Zatem decyzja, czy dany moduł pobudzający powinien zostać wdrożony, czy unikany, powinna opierać się na domenie i intencji, a nie na prewencji.
rwong

1
„millisecondsSince1970” przestał działać dla 32-bitowych liczb wewnętrznych przed końcem stycznia 1970 r., więc wątpię, aby było w nim dużo kodu :-)
gnasher729

1
@rwong Nieco poprawne, ale zakładasz, że preferowane biblioteki reprezentujące punkty czasowe są stabilne. Które nie są. Np. Moment.js
user949300

1

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 Personklasa przechowująca rzeczywiste dane: imię i nazwisko, adres, numer telefonu itp., AddressA także Phoneklasy, 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 nadpisywaniem equalsi getHashcode; 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 geti save, które przyjmą pewne uzasadnione argumenty (np. getMogą mieć przesłonięcia do wyszukiwania według nazwy, wyszukiwania według kodu pocztowego lub po prostu uzyskać wszystko; saveprawdopodobnie zajmie jeden Personi 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ć Persons, i może mieć pewną „logikę biznesową” (np. Może będziemy wymagać, aby wszystkie Personobiekty 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 isConnectedwłaściwość ma już wątpliwą wartość. Co więcej, isConnectedwłaściwość prawdopodobnie będzie błędna tylko wtedy, gdy będzie najbardziej potrzebna: sprawdzanieisConnectedprzed 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ą.


Uważam, że ta linia myślenia jest bardzo podobna do debaty na temat właściwego wykorzystania właściwości C #. Krótko mówiąc, oczekuje się, że właściwości (które mogą być tylko getterami lub parami getter-setter) będą podtrzymywać określoną umowę, na przykład „to, co ustawiasz, to co dostajesz”, „getter powinien być w większości wolny od efektów ubocznych”, „getter powinien unikać błędów wyrzucania” itp. W przeciwieństwie do twojego wniosku, istnieje wiele stanów obiektów, które są korzystne jako właściwości (lub gettery). Przykłady są zbyt liczne, by je tutaj cytować.
rwong

2
@rwong Chciałbym poznać kilka przykładów, powiedzmy dla „normalnej” aplikacji korporacyjnej zaimplementowanej przez jeden zespół. Załóżmy również, dla uproszczenia, że ​​nie są zaangażowane strony trzecie. Wiesz, niezależny system, jak kalendarz, sklep zoologiczny, cokolwiek chcesz.
Robert Bräutigam

1

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?

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 Colori 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 Colorwłaściwość i zareagować na każdą zmianę w niej po wywołaniu metody ReactToDye. Błędem byłoby dla mnie bezpośrednie modyfikowanie Colorkwiatu, 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)czy ChangeMyAddressBecauseIMoved(string newAddress)po prostu string 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, w Customerktórych mieszkałem, mogę użyć setera, aby odpowiednio zmienić mój stan wewnętrzny. W tym przypadku nie ma sensu, Customeraby 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ć.


-1

Co, bez opinii, złamie się, jeśli użyję getterów ...

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 Collectionprzykł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 w Collection, 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 Exceptionklasy Javy , ponieważ ludzie chcieli go używać z różnych ciekawych powodów, których pisarze nie wyobrażali sobie lub nie mogli przewidzieć.


1
Kontrprzykład. Rozważ funkcję, która musi iterować dwa Collections jednocześnie, jak w (A1, B1), (A2, B2), .... Wymaga to wdrożenia „zip”. Jak zaimplementować „zip”, jeśli na jednym z nich zaimplementowano funkcję iteracji Collection- jak uzyskać dostęp do odpowiedniego elementu w drugim?
rwong

@rwong CollectionMusi implementować zipsię 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.
Robert Bräutigam

W takim przypadku oznacza to, że biblioteka musi mieć gettery na Collectionobiekcie, przynajmniej dla własnej implementacji zip. (Oznacza to, że program pobierający musi mieć przynajmniej widoczność pakietu.) A teraz
zależysz

Nie do końca cię rozumiem. CollectionOczywiście mogą uzyskać dostęp do własnego stanu wewnętrznego, lub dokładniej każdy Collectionprzypadek można uzyskać dostępu do innych na stan wewnętrzny. Jest tego samego typu, nie wymaga pobierania.
Robert Bräutigam

Ja też cię słyszę, ale z drugiej strony, jakie jest lepsze rozwiązanie dla osób pobierających? Wiem, że to nie wchodzi w zakres pytania.
coolpasta
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.