Getters i setery są często krytykowane jako nieodpowiednie OO. Z drugiej strony większość kodu OO, który widziałem, zawiera rozległe programy pobierające i ustawiające.
Kiedy pobierający i ustawiający są uzasadnieni? Czy starasz się ich nie używać? Czy są ogólnie nadużywane?
Jeśli twój ulubiony język ma właściwości (mój ma), wówczas takie rzeczy są również uważane za pobierające i ustawiające dla tego pytania. Są to samo z perspektywy metodologii OO. Po prostu mają ładniejszą składnię.
Źródła krytyki Gettera / Settera (niektóre wzięto z komentarzy, aby poprawić ich widoczność):
- http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html
- http://typicalprogrammer.com/?p=23
- http://c2.com/cgi/wiki?AccessorsAreEvil
- http://www.darronschall.com/weblog/2005/03/no-brain-getter-and-setters.cfm
- http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and
- http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html
Aby wyrazić krytykę po prostu: Getters and Setters pozwalają manipulować stanem wewnętrznym obiektów spoza obiektu. To narusza enkapsulację. Tylko sam obiekt powinien dbać o swój stan wewnętrzny.
I przykładowa wersja proceduralna kodu.
struct Fridge
{
int cheese;
}
void go_shopping(Fridge fridge)
{
fridge.cheese += 5;
}
Wersja kodu mutatora:
class Fridge
{
int cheese;
void set_cheese(int _cheese) { cheese = _cheese; }
int get_cheese() { return cheese; }
}
void go_shopping(Fridge fridge)
{
fridge.set_cheese(fridge.get_cheese() + 5);
}
Programy pobierające i ustawiające znacznie skomplikowały kod bez zapewnienia odpowiedniego hermetyzacji. Ponieważ stan wewnętrzny jest dostępny dla innych obiektów, niewiele zyskujemy przez dodanie tych pobierających i ustawiających.
Pytanie zostało wcześniej omówione na temat przepełnienia stosu:
źródło
Getters and setters are often criticized as being not proper OO
- Proszę o cytowanie.Odpowiedzi:
Posiadanie getterów i seterów samo w sobie nie przerywa enkapsulacji. To, co przerywa enkapsulację, to automatyczne dodawanie gettera i settera dla każdego członka danych (każdego pola , w języku Java), bez zastanowienia. Jest to lepsze niż upublicznienie wszystkich członków danych, ale to tylko niewielki krok.
Istotą enkapsulacji nie jest to, że nie powinieneś być w stanie poznać ani zmienić stanu obiektu spoza obiektu, ale że powinieneś mieć rozsądne zasady, aby to zrobić.
Niektóre elementy danych mogą być całkowicie wewnętrzne w stosunku do obiektu i nie powinny mieć modułów pobierających ani ustawiających.
Niektóre elementy danych powinny być tylko do odczytu, więc mogą potrzebować modułów pobierających, ale nie ustawiających.
Niektóre elementy danych mogą wymagać zachowania spójności. W takim przypadku nie zapewniłbyś dla każdego ustawienia, ale pojedynczą metodę ustawiania ich w tym samym czasie, abyś mógł sprawdzić wartości pod kątem spójności.
Niektóre elementy danych mogą wymagać zmiany tylko w określony sposób, na przykład zwiększane lub zmniejszane o określoną kwotę. W takim przypadku podasz metodę
increment()
i / lubdecrement()
metodę, a nie ustawiającą.Jednak inni mogą potrzebować odczytu i zapisu i mieliby zarówno gettera, jak i setera.
Rozważ przykład
class Person
. Powiedzmy, że dana osoba ma nazwisko, numer ubezpieczenia społecznego i wiek. Powiedzmy, że nie zezwalamy ludziom na zmianę nazwiska lub numeru ubezpieczenia społecznego. Jednak wiek osoby powinien być zwiększany o 1 każdego roku. W takim przypadku podasz konstruktor, który zainicjuje nazwę i SSN zgodnie z podanymi wartościami, i który zainicjuje wiek do 0. Podałbyś również metodęincrementAge()
, która zwiększyłaby wiek o 1. Podałbyś także getters dla wszystkich trzech. W tym przypadku nie są wymagane ustawienia.W tym projekcie zezwalasz na sprawdzanie stanu obiektu spoza klasy i pozwalasz na jego zmianę spoza klasy. Nie zezwalasz jednak na arbitralną zmianę stanu. Istnieje polityka, która skutecznie stwierdza, że nazwy i numeru SSN nie można w ogóle zmienić, a wiek można zwiększyć o 1 rok na raz.
Powiedzmy teraz, że dana osoba ma również pensję. Ludzie mogą zmieniać pracę do woli, co oznacza, że ich wynagrodzenie również ulegnie zmianie. Aby wymodelować tę sytuację, nie mamy innego wyjścia, jak zapewnić
setSalary()
metodę! Zezwolenie na dowolną zmianę wynagrodzenia jest w tym przypadku całkowicie rozsądną polityką.Nawiasem mówiąc, w swoim przykładzie, dałbym klasę i metod, zamiast i . Wtedy nadal miałbyś enkapsulację.
Fridge
putCheese()
takeCheese()
get_cheese()
set_cheese()
źródło
Podstawowy powód pobierania i ustawiania w Javie jest bardzo prosty:
Dlatego jeśli chcesz pozwolić, aby pole przechodziło przez interfejs, będziesz potrzebował metody czytnika i zapisu. Są one tradycyjnie nazywane getX i setX dla pola x.
źródło
Od http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and
Styl JavaBean:
Styl OO:
Cóż, wyraźnie wolę to drugie podejście; nie upuszcza szczegółów implementacji, jest prostszy i bardziej zwięzły, a wszystkie potrzebne informacje są zawarte w wywołaniu metody, więc łatwiej jest to zrobić poprawnie. W miarę możliwości wolę także ustawiać członków prywatnych przy użyciu parametrów w konstruktorze.
Twoje pytanie brzmi, kiedy uzasadnienie gettera / settera jest uzasadnione? Być może, gdy potrzebna jest zmiana trybu lub konieczne jest przesłuchanie obiektu w celu uzyskania informacji.
Myśląc o tym, kiedy po raz pierwszy zacząłem kodować w C #, napisałem dużo kodu w stylu Javabeans zilustrowanym powyżej. Ale kiedy zdobywałem doświadczenie w języku, zacząłem robić więcej ustawień członków w konstruktorze i używać metod, które wyglądały bardziej jak powyższy styl OO.
źródło
Kiedy zachowanie „get” i „set” faktycznie pasuje do zachowania w twoim modelu, co praktycznie nigdy nie jest możliwe .
Każde inne użycie jest oszustwem, ponieważ zachowanie domeny biznesowej nie jest jasne.
Edytować
Ta odpowiedź mogła okazać się nieporadna, więc pozwól mi rozwinąć. Powyższe odpowiedzi są w większości poprawne, ale koncentrują się na paradygmatach programistycznych projektowania OO i niektórych, które pomijają duży obraz. Z mojego doświadczenia wynika, że ludzie sądzą, że unikanie spotkań i seterów jest pewną akademicką zasadą dla języków programowania OO (np. Ludzie myślą, że wymiana getterów na właściwości jest wielka)
W rzeczywistości jest to konsekwencja procesu projektowania. Nie musisz wchodzić w interfejsy, enkapsulację i wzorce, kłócić się o to, czy to łamie lub nie łamie tych paradygmatów, a co jest dobrym programowaniem OO. Jedynym punktem, który ostatecznie ma znaczenie, jest to, że jeśli w Twojej domenie nie ma nic, co działałoby w ten sposób, umieszczając je w domenie, nie modelujesz domeny.
Rzeczywistość jest taka, że jest mało prawdopodobne, aby jakikolwiek system w Twojej domenie miał funkcje pobierające i ustawiające. Nie możesz podejść do mężczyzny lub kobiety odpowiedzialnej za płacę i po prostu powiedzieć „Ustaw tę pensję na X” lub „Zdobądź tę pensję” . Takie zachowanie po prostu nie istnieje
Jeśli wstawiasz to do swojego kodu, nie projektujesz systemu tak, aby pasował do modelu Twojej domeny. Tak, to zrywa interfejsy i enkapsulację, ale nie o to chodzi. Chodzi o to, że modelujesz coś, co nie istnieje.
Co więcej, prawdopodobnie brakuje ci ważnego kroku lub procesu, ponieważ prawdopodobnie istnieje powód, dla którego nie mogę po prostu podejść do listy wypłat i powiedzieć, że ustaw tę pensję na X.
Kiedy ludzie używają getterów i seterów, mają tendencję do wypychania reguł tego procesu w niewłaściwe miejsce. To jeszcze bardziej odchodzi od Twojej domeny. Na przykładzie z prawdziwego świata przypomina to pensję, zakładając, że przypadkowa osoba, która weszła, ma pozwolenie na uzyskanie tych wartości, inaczej nie poprosiłby o nie. Domena jest nie tylko taka, jak domena, ale w rzeczywistości kłamie na temat jej właściwości.
źródło
set/get
wynagrodzenie?Zasadniczo gettery i setery są złym pomysłem. Jeśli pole nie jest logicznie częścią interfejsu, a Ty ustawisz go jako prywatny, nie ma sprawy. Jeśli logicznie jest to część interfejsu, a Ty upubliczniasz to nie ma sprawy. Ale jeśli ustawisz go jako prywatny, a następnie odwrócisz się i ponownie uczynisz go efektywnym publicznym, udostępniając narzędzie pobierające i ustawiające, wrócisz do miejsca, w którym zacząłeś, z wyjątkiem tego, że kod jest teraz bardziej szczegółowy i zaciemniony.
Oczywiście są wyjątki. W Javie może być konieczne użycie interfejsów. Standardowa biblioteka Java ma wymagania dotyczące kompatybilności wstecznej, tak wysokie, że przewyższają normalne miary jakości kodu. Możliwe jest nawet, że rzeczywiście masz do czynienia z legendarnym, ale rzadkim przypadkiem, w którym istnieje duża szansa, że możesz później zastąpić zapisane pole obliczeniem w locie, nie naruszając w inny sposób interfejsu. Ale to są wyjątki. Gettery i setery to anty-wzór, który wymaga specjalnego uzasadnienia.
źródło
to, czy pole jest dostępne bezpośrednio czy za pomocą metody, nie jest tak naprawdę ważne.
Niezmienniki klasowe (przydatne) są ważne. Aby je zachować, czasami nie musimy być w stanie zmienić czegoś z zewnątrz. Na przykład. jeśli mamy klasę Kwadrat z separete szerokością i wysokością, zmiana jednego z nich powoduje, że staje się czymś innym niż kwadrat. Potrzebujemy więc metody changeSide Gdyby to był Rectangle, moglibyśmy mieć setters / public field. Ale seter sprawdziłby, czy jego wartość większa niż zero byłaby lepsza.
A w konkretnych językach (np. Java) są powody, dla których potrzebujemy tych metod (interfejsów). Innym powodem tej metody jest zgodność (źródłowa i binarna). Łatwiej je więc dodać, niż pomyśleć, czy wystarczy pole publiczne.
btw. Lubię używać prostych klas przechowywania niezmiennej wartości z publicznymi polami końcowymi.
źródło
Możesz zmienić ustawienia wewnętrzne na cokolwiek, zachowując interfejsy bez zmian. Jeśli twoje interfejsy się nie różnią, kod, który kodujesz, nie ulegnie uszkodzeniu. Nadal możesz zmieniać swoje elementy wewnętrzne, jak chcesz.
źródło
Moje podejście jest takie -
Kiedy spodziewam się, że z czasem skończę z danymi, getter / setter jest uzasadniony. Ponadto, jeśli zachodzi zmiana, często wpycham dane do gettera / settera.
Jeśli jest to struktura POD, pozostawiam wolne miejsca.
Na bardziej abstrakcyjnym poziomie pytanie brzmi: „kto zarządza danymi”, a to zależy od projektu.
źródło
Jeśli używanie programów pobierających i ustawiających wydaje się skomplikowane, problemem może być język, a nie sama koncepcja.
Oto kod z drugiego przykładu napisanego w Ruby:
Zauważ, że wygląda to bardzo podobnie do pierwszego przykładu w Javie? Gdy osoby pobierające i ustawiające są traktowane jak obywatele pierwszej klasy, nie są obowiązkiem w użyciu, a dodatkowa elastyczność może czasem być prawdziwym dobrodziejstwem - na przykład możemy zdecydować o zwróceniu domyślnej wartości dla sera na nowej lodówce:
Oczywiście będzie wiele zmiennych, których w ogóle nie należy ujawniać publicznie. Niepotrzebne ujawnianie zmiennych wewnętrznych pogorszy kod, ale nie można winić tego za pobierające i ustawiające.
źródło
Rozważ
Size
klasę, która ujmuje szerokość i wysokość. Mogę wyeliminować setery za pomocą konstruktora, ale w jaki sposób pomaga mi to narysować prostokątSize
? Szerokość i wysokość nie są danymi wewnętrznymi dla klasy; są to udostępniane dane, które muszą być dostępne dla konsumentów Size.Obiekty składają się z zachowań i stanu - lub atrybutów. Jeśli nie ma stanu narażonego, tylko zachowania są publiczne.
Bez stanu, w jaki sposób posortowałbyś kolekcję obiektów? Jak szukałbyś określonego wystąpienia obiektu? Jeśli używasz tylko konstruktorów, co się stanie, gdy twój obiekt ma długą listę atrybutów?
Żaden parametr metody nie powinien być nigdy używany bez sprawdzania poprawności. Dlatego lenistwo pisze:
Jeśli napiszesz to w ten sposób, przynajmniej przygotowałeś się do użycia settera do sprawdzania poprawności; to lepsze niż pole publiczne - ale ledwo. Ale przynajmniej masz spójny interfejs publiczny, do którego możesz wrócić i wprowadzić reguły sprawdzania poprawności bez łamania kodu klientów.
Gettery, setery, właściwości, mutatory, nazywaj ich jak chcesz, ale są konieczne.
źródło
int
przykład zmienną instancji i wyobraź sobie, że zdecydujesz się dodać regułę sprawdzania poprawności, aby akceptować tylko wartości nieujemne. Problem polega na tym, że kod klienta może już polegać na możliwości ustawienia go na wartość ujemną ...Jeśli gettery i settery naruszają enkapsulację i prawdziwe OO, to mam poważne kłopoty.
Zawsze czułem, że przedmiot reprezentuje wszystko, co przychodzi ci na myśl, czego potrzebujesz.
Właśnie skończyłem pisać program, który generuje Mazes w Javie, i mam klasę reprezentującą „Maze Squares”. Mam dane w tej klasie, które reprezentują współrzędne, ściany i booleany itp.
Muszę mieć sposób na zmianę / manipulację / dostęp do tych danych! Co mam zrobić bez getterów i seterów? Java nie korzysta z właściwości, a ustawienie wszystkich moich danych lokalnych dla tej klasy na publiczne jest ZDECYDOWANIE naruszeniem enkapsulacji i OO.
źródło
Po pierwsze, są zasadniczo dwa typy obiektów, które będę komentować, wartości i typy usług.
Typy usług nigdy nie powinny mieć ustawień, bez względu na wymagane zależności nie można ich uzyskać. Najlepszym sposobem na przekazanie zależności jest użycie konstruktora lub fabryki, dzięki czemu wszystkie instancje są w pełni uformowane od początku, jasne i proste.
Typy wartości również powinny być niezmienne, z drugiej strony zdarzają się sytuacje, w których jest to niepraktyczne, takie jak ORM lub inne mapowanie, np. Z widżetu na obiekt. Wszystkie inne typy wartości, które są przekazywane dookoła systemu z jednej warstwy lub części na drugą, powinny być niezmienne i nie powinny mieć ustawiających.
źródło
Getter / Setter jest uzasadniony, tak jak każda inna metoda publiczna jest uzasadniona. Uzasadnieniem jest głównie dlatego, że chcemy zapewnić interfejs do świata zewnętrznego, który określa sposób, w jaki nasza klasa współdziała z innymi klasami. Robimy to głównie dlatego, że chcemy zmniejszyć sprzężenie między osobnymi podmiotami. Zmniejszenie sprzężenia jest dobre z wielu powodów:
źródło
Tak, metody pobierające i ustawiające to anty-wzorzec w programowaniu obiektowym i nigdy nie powinny być używane: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . Krótko mówiąc, nie pasują do paradygmatu obiektowego, ponieważ zachęcają do traktowania obiektu jak struktury danych. Co jest głównym nieporozumieniem.
źródło
Mam nadzieję, że wszystko, co IDE opracowane przez firmę, która rozwija język programowania automatycznie generuje, nie narusza „Zasad orientacji obiektowej”.
To powiedziawszy, za każdym razem, gdy masz zmienną o zasięgu publicznym, użyj gettera i setera i jesteś złoty. Wydaje mi się, że jest niezbędnym elementem niezwykle zorientowanej obiektowo zasady enkapsulacji - nawet jeśli wygląda na dużo niepotrzebnego kodu.
Powodem ich użycia jest to, że może jeszcze nastąpić właściwe hermetyzowanie i tak długo, jak długo wyodrębnisz warstwę, która pozwala osobie małpującej z twoim przedmiotem poczuć twój obiekt, możesz zabrać ten obiekt bez jego wiedzy, prawda?
Wystarczy powiedzieć, że podzielony dom nie może znieść, jeden z lokatorów OO nie chce się włączyć i nie można służyć zarówno enkapsulacji, jak i przedwczesnej optymalizacji.
źródło
Myślałem, że będziemy tu mieli więcej zwykłych odpowiedzi?
Gettery i setery są na ogół bardzo OOP (każdy, kto mówi cokolwiek innego, jest źle, tak po prostu).
Chociaż musisz pamiętać, aby nie przesadzać z inżynierem, nigdy nie twórz gettera ani setera, który nie jest potrzebny. Wszelkie informacje, których nie będziesz używać przez inną klasę, dla których nie powinieneś mieć gettera ani setera.
Chociaż powodem, dla którego nie upubliczniasz pól, a zamiast tego masz metody pobierające i ustawiające, są w większości:
Nie można wykonać odpowiednich testów przy użyciu pól. Getters / setery są pod tym względem znacznie lepsze.
Niemożliwe jest prawidłowe użycie pól w ustawieniach programowania w czasie rzeczywistym. Zsynchronizowane urządzenia pobierające / ustawiające to właściwy sposób.
Temat interfejsu (już wyjaśniony, więc nie powiem).
Łatwiej jest go używać, gdy ponownie używasz systemu.
Znacznie trudniej jest wiedzieć, które klasy używają twojej klasy i co się zepsuje, jeśli zmienisz pole niż getter / setter.
Dlatego tylko raz korzystasz z pola publicznego zamiast gettera / setera, kiedy nie tworzysz oficjalnego projektu, ale robisz to dla zabawy i nie możesz zawracać sobie głowy geterem / seterami.
źródło