Jaka jest zaleta używania metod pobierających i ustawiających - które tylko pobierają i ustawiają - zamiast po prostu używania pól publicznych dla tych zmiennych?
Jeśli osoby pobierające i ustawiające kiedykolwiek robią coś więcej niż zwykłe polecenie get / set, mogę to szybko rozgryźć, ale nie jestem w 100% pewien, jak:
public String foo;
jest gorszy niż:
private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }
Podczas gdy ten pierwszy wymaga o wiele mniej kodu typu „kocioł”.
Odpowiedzi:
W rzeczywistości istnieje wiele dobrych powodów, aby rozważyć użycie akcesoriów zamiast bezpośredniego ujawnienia pól klasy - poza samym argumentem hermetyzacji i ułatwieniem przyszłych zmian.
Oto niektóre z powodów, o których wiem:
źródło
public
metody akcesora powodują duplikację kodu i ujawniają informacje. Publiczne urządzenia dostępowe nie są konieczne do zestawiania (serializacji). W niektórych językach (Java) wpublic
akcesoriach pobierania nie można zmienić typu zwrotu bez rozbijania klas zależnych. Ponadto uważam, że są one sprzeczne z zasadami OO. Zobacz także: stackoverflow.com/a/1462424/59087plane.turnTo(dir); plane.setSpeed(spd); plane.setTargetAltitude(alt); plane.getBreaks().release();
chcę powiedziećplane.takeOff(alt)
. To, jakie wewnętrzne pola danych należy zmienić, aby wprowadzić samolot wtakingOff
tryb, nie jest moim zmartwieniem. I jakie inne obiekty (breaks
) metoda powiadamia, że nie chcę też wiedzieć.Ponieważ za 2 tygodnie (miesiące, lata), kiedy zdasz sobie sprawę, że twój setter musi zrobić coś więcej niż tylko ustawić wartość, zdasz sobie również sprawę, że nieruchomość była używana bezpośrednio w 238 innych klasach :-)
źródło
Pole publiczne nie jest gorsze niż para getter / setter, która nie robi nic poza zwrotem pola i przypisaniem do niego. Po pierwsze, jasne jest, że (w większości języków) nie ma żadnej różnicy funkcjonalnej. Każda różnica musi wynikać z innych czynników, takich jak łatwość konserwacji lub czytelność.
Często wymieniana zaleta par getter / setter nie jest. Istnieje twierdzenie, że możesz zmienić implementację, a twoi klienci nie muszą być ponownie kompilowani. Podobno setery pozwalają później dodawać funkcje takie jak sprawdzanie poprawności, a Twoi klienci nawet nie muszą o tym wiedzieć. Jednak dodanie sprawdzania poprawności do setera jest zmianą jego warunków wstępnych, naruszeniem poprzedniej umowy , która po prostu brzmiała: „możesz tu wstawić wszystko i możesz uzyskać to samo później od gettera”.
Teraz, gdy złamałeś umowę, zmiana każdego pliku w bazie kodu jest czymś, czego powinieneś chcieć, a nie unikać. Jeśli tego unikniesz, przyjmujesz, że cały kod zakładał, że umowa na te metody była inna.
Gdyby nie taka była umowa, interfejs pozwalał klientom na umieszczenie obiektu w niepoprawnych stanach. To jest dokładnie odwrotność enkapsulacji. Jeśli to pole nie mogło tak naprawdę być ustawione na coś od samego początku, dlaczego nie było tam sprawdzania poprawności od samego początku?
Ten sam argument dotyczy innych rzekomych zalet tych par getter / setter: jeśli później zdecydujesz się zmienić ustawianą wartość, zrywasz kontrakt. Jeśli zastąpisz domyślną funkcjonalność w klasie pochodnej, w sposób wykraczający poza kilka nieszkodliwych modyfikacji (takich jak rejestrowanie lub inne nieobserwowalne zachowanie), zrywasz kontrakt klasy podstawowej. Jest to naruszenie zasady substytucyjności Liskowa, która jest postrzegana jako jedna z zasad OO.
Jeśli klasa ma te głupie pobierające i ustawiające dla każdego pola, to jest to klasa, która nie ma żadnych niezmienników, żadnej umowy . Czy to naprawdę projekt obiektowy? Jeśli jedyną klasą są te pobierające i ustawiające, to tylko głupi posiadacz danych, a głupi posiadacze danych powinni wyglądać jak głupi posiadacze danych:
Dodanie par getter / setter tranzytowych do takiej klasy nie dodaje żadnej wartości. Inne klasy powinny zapewniać znaczące operacje, a nie tylko operacje, które pola już zapewniają. W ten sposób możesz definiować i utrzymywać przydatne niezmienniki.
Istnieją powody, aby używać pobierających i ustawiających, ale jeśli te powody nie istnieją, tworzenie par pobierających / ustawiających w imię fałszywych bogów hermetyzacji nie jest dobrą rzeczą. Prawidłowe powody, dla których gettery lub settery obejmują rzeczy często wymieniane jako potencjalne zmiany, które możesz wprowadzić później, takie jak walidacja lub różne wewnętrzne reprezentacje. A może wartość powinna być czytelna dla klientów, ale nie do zapisu (na przykład odczytanie wielkości słownika), więc prosty getter jest dobrym wyborem. Ale powody te powinny istnieć przy dokonywaniu wyboru, a nie tylko jako potencjalna rzecz, której możesz chcieć później. To jest przykład YAGNI ( You Ain't Go Need Need ).
źródło
private
pole. Jeśli klasa nie ma elementów ustawiających, łatwo jest uczynić ją niezmienną.Wiele osób mówi o zaletach getterów i seterów, ale chcę grać w adwokata diabła. W tej chwili debuguję bardzo duży program, w którym programiści postanowili zrobić wszystko, aby pobrać i ustawić. To może wydawać się miłe, ale to koszmar inżynierii odwrotnej.
Powiedzmy, że przeglądasz setki linii kodu i natrafiasz na to:
Jest to pięknie po prostu kawałek kodu, dopóki nie uświadomisz sobie, że jest on ustawiaczem. Teraz podążasz za tym ustawiaczem i odkrywasz, że ustawia on także person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName i wywołuje person.update (), który wysyła zapytanie do bazy danych itp. Och, to gdzie miał miejsce wyciek pamięci.
Zrozumienie lokalnego fragmentu kodu na pierwszy rzut oka jest ważną właściwością dobrej czytelności, którą pobierają i pobudzają programy ustawiające. Dlatego staram się ich unikać, kiedy tylko mogę, i minimalizować to, co robią, kiedy ich używam.
źródło
person.name = "Joe";
się nazywa. Informacje nie zawierają żadnych cruftów, podświetlanie składni pokazuje, że jest to pole, a refaktoryzacja jest prostsza (nie trzeba refaktoryzować dwóch metod i pola). Po drugie, wszystkie te zmarnowane cykle mózgowe myślące „getIsValid ()” lub powinno być „isValid ()” itp. Można zapomnieć. Nie potrafię wymyślić singla, gdy getter / setter uratował mnie przed błędem.W świecie czysto zorientowanym obiektowo getters and setters to straszna anty-wzór . Przeczytaj ten artykuł: Getters / Setters. Zło. Okres . W skrócie, zachęcają programistów do myślenia o obiektach jak o strukturach danych, a tego rodzaju myślenie jest czysto proceduralne (jak w języku COBOL lub C). W języku obiektowym nie ma struktur danych, a jedynie obiekty, które ujawniają zachowanie (nie atrybuty / właściwości!)
Więcej informacji na ich temat można znaleźć w rozdziale 3.5 eleganckich obiektów (moja książka o programowaniu obiektowym).
źródło
new Dog()
nie jest psem. Jest to obiekt przechowujący informacje o psie. W przypadku tego zastosowania naturalne jest, że można skorygować nieprawidłowo zarejestrowaną wagę.Jest wiele powodów. Moim ulubionym jest, gdy musisz zmienić zachowanie lub regulować, co możesz ustawić na zmiennej. Załóżmy na przykład, że masz metodę setSpeed (int speed). Ale chcesz, abyś mógł ustawić maksymalną prędkość 100. Zrobiłbyś coś takiego:
Co teraz, jeśli WSZYSTKO w kodzie używałeś pola publicznego, a następnie zdałeś sobie sprawę, że potrzebujesz powyższego wymagania? Baw się dobrze, polując na każde użycie pola publicznego zamiast modyfikować setera.
Moje 2 centy :)
źródło
while(speed < 200) { do_something(); accelerate(); }
)myCar.setSpeed(157);
i po kilku liniachspeed = myCar.getSpeed();
A teraz ... Życzę szczęśliwego debugowania, próbując zrozumieć, dlaczegospeed==100
to powinno być157
Jedną z zalet akcesoriów i mutatorów jest to, że można przeprowadzić walidację.
Na przykład, jeśli
foo
byłby publiczny, mógłbym łatwo ustawić go,null
a następnie ktoś inny mógłby spróbować wywołać metodę na obiekcie. Ale już go tam nie ma! DziękisetFoo
metodzie mogłem upewnić się, żefoo
nigdy nie był ustawionynull
.Akcesoria i mutatory pozwalają również na enkapsulację - jeśli nie powinieneś widzieć wartości po jej zbiorze (być może jest ustawiony w konstruktorze, a następnie używany przez metody, ale nigdy nie powinien zostać zmieniony), nikt nigdy nie zobaczy. Ale jeśli możesz zezwolić innym klasom na zobaczenie lub zmianę, możesz zapewnić odpowiedni akcesor i / lub mutator.
źródło
Zależy od twojego języka. Oznaczyłeś to „obiektowo”, a nie „Java”, więc chciałbym zauważyć, że odpowiedź ChssPly76 zależy od języka. Na przykład w Pythonie nie ma powodu, aby używać programów pobierających i ustawiających. Jeśli chcesz zmienić zachowanie, możesz użyć właściwości, która otacza getter i setter wokół podstawowego dostępu do atrybutów. Coś takiego:
źródło
obj.set_attr('foo')
) są z natury lepsze od znaków równości (obj.attr = 'foo'
). Dostęp publiczny to dostęp publiczny.obj.attr = 'foo'
po prostu ustawia zmienną bez niczego innegoobj.setAttr('foo')
„ustawia zmienną tak, aby nic się nie działo”? Jeśli jest to metoda publiczna, to jest to metoda publiczna. Jeśli użyjesz go do osiągnięcia jakiegoś efektu ubocznego i jest on publiczny, to lepiej móc liczyć na wszystko, co działa tak, jakby wystąpił tylko ten zamierzony efekt uboczny (ze wszystkimi innymi szczegółami implementacji i innymi efektami ubocznymi, wykorzystaniem zasobów, cokolwiek innego , ukryte przed obawami użytkownika). W Pythonie nie jest to absolutnie inaczej. Składnia Pythona do osiągnięcia efektu jest po prostu prostsza.Cóż, chcę tylko dodać, że nawet jeśli czasami są one niezbędne do hermetyzacji i bezpieczeństwa twoich zmiennych / obiektów, jeśli chcemy zakodować prawdziwy program obiektowy, musimy przestać nadużywać akcesoriów , bo czasem bardzo zależy na nich, gdy nie jest to naprawdę konieczne i czyni to prawie tak samo, jakbyśmy podali zmienne do wiadomości publicznej.
źródło
Dzięki, to naprawdę wyjaśniło moje myślenie. Oto (prawie) 10 (prawie) dobrych powodów, by NIE używać getterów i setterów:
Ostatnie trzy, które właśnie opuszczam (N / A lub D / C) ...
źródło
Wiem, że jest trochę za późno, ale myślę, że są ludzie, którzy są zainteresowani wydajnością.
Zrobiłem mały test wydajności. Napisałem klasę „NumberHolder”, która, no cóż, zawiera liczbę całkowitą. Możesz odczytać tę
anInstance.getNumber()
liczbę całkowitą za pomocą metody gettera lub bezpośrednio uzyskując dostęp do numeru za pomocąanInstance.number
. Mój program odczytuje liczbę 1 000 000 000 razy, w obie strony. Proces ten powtarza się pięć razy i czas jest drukowany. Mam następujący wynik:(Czas 1 to bezpośredni sposób, czas 2 to pobierający)
Widzisz, getter jest (prawie) zawsze nieco szybszy. Potem próbowałem z różną liczbą cykli. Zamiast 1 miliona wykorzystałem 10 milionów i 0,1 miliona. Wyniki:
10 milionów cykli:
Przy 10 milionach cykli czasy są prawie takie same. Oto 100 tysięcy (0,1 miliona) cykli:
Również przy różnych ilościach cykli getter jest trochę szybszy niż normalnie. Mam nadzieję, że to ci pomogło.
źródło
Nie używaj ustawiaczy getterów, chyba że jest to potrzebne do bieżącej dostawy. Nie myśl zbyt wiele o tym, co stanie się w przyszłości, jeśli coś, co należy zmienić, to żądanie zmiany w większości aplikacji produkcyjnych, systemów.
Myśl prosto, łatwo, w razie potrzeby dodaj złożoność.
Nie skorzystałbym z niewiedzy właścicieli firm o głębokiej wiedzy technicznej, ponieważ uważam to za słuszne lub podoba mi się to podejście.
Mam ogromny system napisany bez ustawiaczy getterów tylko z modyfikatorami dostępu i niektórymi metodami do sprawdzania poprawności logiki. Jeśli absolutnie potrzebujesz. Użyj czegokolwiek.
źródło
Używamy getterów i seterów:
Metody pobierające i ustawiające są publicznymi interfejsami umożliwiającymi dostęp do członków klasy prywatnej.
Mantra enkapsulacji
Mantra enkapsulacji ma na celu upublicznienie pól i metod.
Mimo że metody pobierające i ustawiające nie dodają nowej funkcjonalności, możemy zmienić zdanie, by wrócić później, aby stworzyć tę metodę
Gdziekolwiek można użyć wartości, można dodać metodę zwracającą tę wartość. Zamiast:
posługiwać się
W kategoriach laika
Załóżmy, że musimy przechowywać szczegóły tego
Person
. ToPerson
ma polaname
,age
isex
. Rozwiązanie to polega na tworzeniu metodname
,age
isex
. Teraz, jeśli potrzebujemy utworzyć inną osobę, konieczne staje się stworzenie metodyname
,age
,sex
wszystko od nowa.Zamiast tego możemy stworzyć fasolę za
class(Person)
pomocą metod pobierających i ustawiających. Więc jutro możemy po prostu tworzyć obiekty tej Fasoli,class(Person class)
ilekroć potrzebujemy dodać nową osobę (patrz rysunek). Dlatego ponownie wykorzystujemy pola i metody klasy fasoli, co jest znacznie lepsze.źródło
Spędziłem sporo czasu zastanawiając się nad sprawą Java i uważam, że prawdziwymi przyczynami są:
Innymi słowy, jedynym sposobem na określenie pola w interfejsie jest dostarczenie metody zapisu nowej wartości oraz metody odczytu bieżącej wartości.
Te metody to niesławny pobieracz i seter ...
źródło
Może być przydatny do leniwego ładowania. Powiedzmy, że przedmiot jest przechowywany w bazie danych i nie chcesz go zdobyć, chyba że jest to potrzebne. Jeśli obiekt zostanie pobrany przez moduł pobierający, wówczas obiekt wewnętrzny może być pusty, dopóki ktoś go nie poprosi, a następnie można go pobrać przy pierwszym wywołaniu funkcji pobierającej.
Miałem klasę strony bazowej w projekcie, który został mi przekazany, który ładował niektóre dane z kilku różnych wywołań usługi internetowej, ale dane w tych wywołaniach usługi internetowej nie zawsze były używane na wszystkich stronach potomnych. Usługi sieciowe, ze wszystkimi korzyściami, są pionierami nowych definicji „powolnego”, więc nie musisz nawiązywać połączeń z usługami internetowymi, jeśli nie musisz.
Przeniosłem się z pól publicznych do getters, a teraz getters sprawdzają pamięć podręczną, a jeśli jej nie ma, zadzwoń do serwisu WWW. Dzięki niewielkiemu pakowaniu udało się zapobiec wielu połączeniom z serwisem internetowym.
Tak więc getter ratuje mnie przed próbą ustalenia na każdej stronie podrzędnej, czego będę potrzebować. Jeśli tego potrzebuję, dzwonię do pobierającego, a on szuka go dla mnie, jeśli jeszcze go nie mam.
źródło
Do tej pory brakowało jednego aspektu w odpowiedziach, specyfikacji dostępu:
źródło
EDYCJA: Odpowiedziałem na to pytanie, ponieważ wiele osób uczy się programowania, a większość odpowiedzi jest bardzo kompetentna technicznie, ale nie jest tak łatwo zrozumieć, jeśli jesteś początkującym. Wszyscy byliśmy początkującymi, więc pomyślałem, że spróbuję swoich sił, by uzyskać bardziej przyjazną odpowiedź dla początkujących.
Dwa główne to polimorfizm i walidacja. Nawet jeśli to tylko głupia struktura danych.
Powiedzmy, że mamy tę prostą klasę:
Bardzo prosta klasa, która przechowuje ilość płynu i jaka jest jego pojemność (w mililitrach).
Co się stanie, gdy to zrobię:
Nie spodziewałbyś się, że to zadziała, prawda? Chcesz, żeby była jakaś kontrola zdrowia psychicznego. Co gorsza, co jeśli nigdy nie podałem maksymalnej pojemności? Och, kochanie, mamy problem.
Ale jest też inny problem. Co jeśli butelki byłyby tylko jednym rodzajem pojemnika? Co gdybyśmy mieli kilka pojemników, wszystkie o pojemnościach i ilościach napełnionych płynem? Gdybyśmy tylko mogli stworzyć interfejs, moglibyśmy pozwolić, aby reszta naszego programu zaakceptowała ten interfejs, a butelki, jerrycany i wszelkiego rodzaju rzeczy po prostu działałyby zamiennie. Czy nie byłoby lepiej? Ponieważ interfejsy wymagają metod, jest to również dobra rzecz.
Skończylibyśmy z czymś takim jak:
Świetny! A teraz zmieniamy Butelkę na to:
Pozostawię definicję BottleOverflowException jako ćwiczenie czytelnikowi.
Teraz zauważ, o ile bardziej jest to solidne. Możemy teraz obsługiwać dowolny rodzaj pojemnika w naszym kodzie, akceptując LiquidContainer zamiast Bottle. A jak te butelki radzą sobie z tego rodzaju rzeczami, wszystko może się różnić. Możesz mieć butelki, które zapisują swój stan na dysk, gdy się zmienia, lub butelki, które oszczędzają w bazach danych SQL lub GNU wiedzą co jeszcze.
A wszystko to może mieć różne sposoby radzenia sobie z różnymi sekcjami. Butelka po prostu sprawdza, a jeśli jest przepełniona, zgłasza wyjątek RuntimeException. Ale to może być niewłaściwa rzecz. (Istnieje użyteczna dyskusja na temat obsługi błędów, ale celowo utrzymuję ją bardzo prosto. Ludzie w komentarzach prawdopodobnie zwrócą uwagę na wady tego uproszczonego podejścia;))
I tak, wydaje się, że przechodzimy od bardzo prostego pomysłu do szybkiego uzyskiwania znacznie lepszych odpowiedzi.
Należy również pamiętać, że nie można zmienić pojemności butelki. Teraz jest osadzony w kamieniu. Możesz to zrobić z int, deklarując to jako ostateczne. Ale jeśli to była lista, możesz ją opróżnić, dodać do niej nowe rzeczy i tak dalej. Nie możesz ograniczyć dostępu do dotykania wnętrzności.
Jest też trzecia rzecz, do której nie wszyscy się zwracali: pobierający i ustawiający używają wywołań metod Oznacza to, że wszędzie wyglądają jak zwykłe metody. Zamiast mieć dziwną specyficzną składnię dla DTO i innych rzeczy, wszędzie masz tę samą rzecz.
źródło
W językach, które nie obsługują „właściwości” (C ++, Java) lub wymagają ponownej kompilacji klientów przy zmianie pól na właściwości (C #), łatwiej jest modyfikować metody get / set. Na przykład dodanie logiki sprawdzania poprawności do metody setFoo nie będzie wymagało zmiany publicznego interfejsu klasy.
W językach obsługujących „prawdziwe” właściwości (Python, Ruby, może Smalltalk?) Nie ma sensu pobierać / ustawiać metod.
źródło
Jedna z podstawowych zasad projektowania OO: Encapsulation!
Daje to wiele korzyści, z których jedną jest to, że możesz zmienić implementację gettera / settera za kulisami, ale każdy konsument o tej wartości będzie kontynuował pracę, dopóki typ danych pozostanie taki sam.
źródło
Powinieneś używać pobierających i ustawiających, gdy:
Jest to więc bardzo rzadko ogólne pytanie OO; jest to pytanie specyficzne dla języka, z różnymi odpowiedziami dla różnych języków (i różnych przypadków użycia).
Z punktu widzenia teorii OO pobierający i ustawiający są bezużyteczni. Interfejs twojej klasy jest tym, co robi, a nie stanem. (Jeśli nie, napisałeś niewłaściwą klasę.) W bardzo prostych przypadkach, gdy to, co robi klasa, to po prostu np. Reprezentuje punkt we współrzędnych prostokątnych, * atrybuty są częścią interfejsu; gettery i setery po prostu to chmurnieją. Ale w niczym innym, jak w bardzo prostych przypadkach, ani atrybuty, ani metody pobierające i ustawiające nie są częścią interfejsu.
Innymi słowy: jeśli uważasz, że konsumenci z twojej klasy nie powinni nawet wiedzieć, że masz
spam
atrybut, a tym bardziej być w stanie go zmienić nie chcąc, to daj imset_spam
metody jest ostatnią rzeczą, którą chcesz zrobić.* Nawet w przypadku tej prostej klasy niekoniecznie trzeba zezwolić na ustawienie wartości
x
iy
. Jeśli jest to naprawdę klasa, nie powinien mieć takich metodtranslate
,rotate
itp? Jeśli to tylko klasa, ponieważ twój język nie ma rekordów / struktur / nazwanych krotek, to tak naprawdę nie jest to kwestia OO…Ale nikt nigdy nie robi ogólnego projektu OO. Robią projektowanie i wdrażanie w określonym języku. W niektórych językach osoby pobierające i ustawiające są dalekie od bezużytecznych.
Jeśli twój język nie ma właściwości, to jedynym sposobem na przedstawienie czegoś, co koncepcyjnie jest atrybutem, ale w rzeczywistości jest obliczone lub sprawdzone itp., Jest użycie metod pobierających i ustawiających.
Nawet jeśli Twój język ma właściwości, mogą wystąpić przypadki, w których są niewystarczające lub nieodpowiednie. Na przykład, jeśli chcesz pozwolić podklasom kontrolować semantykę atrybutu, w językach bez dostępu dynamicznego, podklasa nie może zastąpić obliczonej właściwości atrybutem.
Co do „co jeśli później chcę zmienić implementację?” pytanie (które powtarza się wiele razy w różnych sformułowaniach zarówno w pytaniu PO, jak i zaakceptowanej odpowiedzi): Jeśli tak naprawdę jest to czysta zmiana implementacji, a ty zacząłeś od atrybutu, możesz zmienić go na właściwość bez wpływu na interfejs. Chyba że twój język tego nie obsługuje. Więc to naprawdę tak samo.
Ważne jest również przestrzeganie idiomów używanego języka (lub frameworka). Jeśli napiszesz piękny kod w języku Ruby w języku C #, każdy doświadczony programista w języku C #, inny niż ty, będzie miał problemy z odczytaniem go, a to źle. Niektóre języki mają silniejsze kultury wokół swoich konwencji niż inne. I może nie być przypadkiem, że Java i Python, które znajdują się na przeciwległych krańcach spektrum, jak idiomatyczni są, mają dwie najsilniejsze kultury.
Poza ludzkimi czytelnikami będą biblioteki i narzędzia, które oczekują od ciebie przestrzegania konwencji i utrudniają ci życie, jeśli tego nie zrobisz. Przechwytywanie widgetów Konstruktora interfejsów do niczego poza właściwościami ObjC lub korzystanie z niektórych fałszywych bibliotek Java bez programów pobierających, tylko utrudnia życie. Jeśli narzędzia są dla Ciebie ważne, nie walcz z nimi.
źródło
Z punktu widzenia projektowania obiektowego obie alternatywy mogą być szkodliwe dla utrzymania kodu przez osłabienie enkapsulacji klas. Do dyskusji możesz zajrzeć do tego doskonałego artykułu: http://typicalprogrammer.com/?p=23
źródło
Metody pobierające i ustawiające są metodami dostępowymi, co oznacza, że są one ogólnie publicznym interfejsem do zmiany członków klasy prywatnej. Do zdefiniowania właściwości używa się metod pobierających i ustawiających. Dostęp do metod pobierających i ustawiających uzyskuje się jako właściwości poza klasą, nawet jeśli definiuje się je w klasie jako metody. Te właściwości poza klasą mogą mieć inną nazwę niż nazwa właściwości w klasie.
Korzystanie z metod pobierających i ustawiających ma pewne zalety, takie jak możliwość tworzenia członków o wyrafinowanej funkcjonalności, do której można uzyskać dostęp podobnie jak właściwości. Pozwalają również tworzyć właściwości tylko do odczytu i tylko do zapisu.
Mimo że metody pobierające i ustawiające są przydatne, należy uważać, aby ich nie nadużywać, ponieważ między innymi mogą utrudnić utrzymanie kodu w niektórych sytuacjach. Zapewniają również dostęp do implementacji klasy, podobnie jak członkowie publiczni. Praktyka OOP odradza bezpośredni dostęp do nieruchomości w klasie.
Pisząc klasy, zawsze zachęcamy do uczynienia jak największej liczby zmiennych instancji prywatnymi i dodania odpowiednio metod pobierających i ustawiających. Wynika to z tego, że kilka razy możesz nie chcieć pozwalać użytkownikom zmieniać określonych zmiennych w swoich klasach. Na przykład, jeśli masz prywatną metodę statyczną, która śledzi liczbę instancji utworzonych dla określonej klasy, nie chcesz, aby użytkownik modyfikował ten licznik za pomocą kodu. Tylko instrukcja konstruktora powinna zwiększać tę zmienną za każdym razem, gdy zostanie wywołana. W tej sytuacji możesz utworzyć zmienną instancji prywatnej i zezwolić na metodę gettera tylko dla zmiennej licznika, co oznacza, że użytkownicy mogą pobierać bieżącą wartość tylko za pomocą metody getter i nie będą mogli ustawić nowych wartości za pomocą metody setter.
źródło
Kod ewoluuje .
private
jest świetny, gdy potrzebujesz ochrony członka danych . W końcu wszystkie klasy powinny być swego rodzaju „miniprogramami”, które mają dobrze zdefiniowany interfejs , którego nie można po prostu skręcić z elementami wewnętrznymi .To powiedziawszy, rozwój oprogramowania nie polega na ustaleniu ostatecznej wersji klasy, tak jakbyś naciskał żeliwną statuę przy pierwszej próbie. Podczas pracy z nim kod przypomina bardziej glinę. Ewoluuje wraz z rozwojem i dowiaduje się więcej o problemie, który rozwiązujesz. Podczas programowania klasy mogą ze sobą współdziałać, niż powinny (zależność, którą planujesz uwzględnić), łączyć się ze sobą lub dzielić. Myślę więc, że debata sprowadza się do ludzi, którzy nie chcą pisać religijnie
Więc masz:
Zamiast
Jest nie tylko
getVar()
wizualnie hałaśliwy, ale daje iluzję, któragettingVar()
jest w jakiś sposób bardziej złożonym procesem niż w rzeczywistości. To, jak postrzegasz świętość,var
jest szczególnie mylące dla użytkownika twojej klasy, jeśli ma on ustawianie passthru - wtedy wygląda na to, że stawiasz te bramy, aby „chronić” coś, co według ciebie jest cenne, (świętośćvar
), ale nawet ty przyznajeszvar
ochronę, nie jest wiele warta, że ktoś może po prostu wejść iset
var
do jakiejkolwiek wartości, jakiej pragnie, nawet bez zerknięcia na to, co robią.Więc programuję w następujący sposób (zakładając podejście typu „zwinnego” - tj. Kiedy piszę kod nie wiedząc dokładnie co on będzie robił / nie mam czasu ani doświadczenia, aby zaplanować rozbudowany zestaw interfejsu w stylu wodospadu):
1) Zacznij od wszystkich członków publicznych dla podstawowych obiektów z danymi i zachowaniem. Dlatego w całym moim „przykładowym” kodzie C ++ zauważysz, że używam
struct
zamiast niegoclass
wszędzie.2) Gdy zachowanie wewnętrzne obiektu dla elementu danych staje się wystarczająco złożone (na przykład lubi utrzymywać wewnętrzny
std::list
porządek w jakiejś kolejności), zapisywane są funkcje typu akcesor. Ponieważ programuję sam, nie zawsze ustawiam członkaprivate
od razu, ale gdzieś poniżej ewolucji klasy członek zostanie „awansowany” do jednegoprotected
lub drugiegoprivate
.3) Klasy, które są w pełni rozwinięte i mają ścisłe zasady dotyczące swoich elementów wewnętrznych (tj . Dokładnie wiedzą, co robią i nie wolno ci „pieprzyć” (termin techniczny) z ich
class
elementami wewnętrznymi) otrzymują oznaczenie, domyślni członkowie prywatni, i tylko kilku wybranych członków może byćpublic
.Uważam, że takie podejście pozwala mi uniknąć siedzenia i religijnego pisania getterów / seterów, gdy wielu członków danych migruje, przemieszcza się itp. Na wczesnych etapach ewolucji klasy.
źródło
Istnieje dobry powód, aby rozważyć użycie akcesoriów, ponieważ nie ma dziedziczenia własności. Zobacz następny przykład:
Wynik:
źródło
Gettery i settery są używane do implementacji dwóch podstawowych aspektów programowania obiektowego:
Załóżmy, że mamy klasę Pracowników:
Tutaj szczegóły implementacji pełnej nazwy są ukryte przed użytkownikiem i nie są dostępne bezpośrednio dla użytkownika, w przeciwieństwie do atrybutu publicznego.
źródło
Innym zastosowaniem (w językach obsługujących właściwości) jest to, że setery i gettery mogą sugerować, że operacja nie jest trywialna. Zazwyczaj chcesz uniknąć robienia czegokolwiek kosztownego obliczeniowo we właściwości.
źródło
execute
lubbuild
metody.Jedną stosunkowo nowoczesną zaletą programów pobierających / ustawiających jest to, że ułatwia przeglądanie kodu w edytorach tagowanych (indeksowanych). Np. Jeśli chcesz zobaczyć, kto ustawia członka, możesz otworzyć hierarchię połączeń setera.
Z drugiej strony, jeśli członek jest publiczny, narzędzia nie umożliwiają filtrowania dostępu do odczytu / zapisu do członka. Musisz więc przebrnąć przez wszystkie zastosowania członka.
źródło
W języku obiektowym metody i ich modyfikatory dostępu deklarują interfejs dla tego obiektu. Pomiędzy konstruktorem a metodami akcesora i mutatora deweloper może kontrolować dostęp do wewnętrznego stanu obiektu. Jeśli zmienne są po prostu zadeklarowane jako publiczne, nie ma sposobu na regulację tego dostępu. A kiedy używamy seterów, możemy ograniczyć użytkownika do potrzebnych nam danych wejściowych. Oznacza to, że feed dla tej bardzo zmiennej będzie przechodził przez właściwy kanał, a kanał jest przez nas predefiniowany. Więc bezpieczniej jest używać seterów.
źródło
Dodatkowo ma to na celu „zabezpieczenie przyszłości” twojej klasy. W szczególności zmiana z pola na właściwość jest przerwą ABI, więc jeśli później zdecydujesz, że potrzebujesz więcej logiki niż tylko „ustaw / pobierz pole”, to musisz przerwać ABI, co oczywiście stwarza problemy dla czegokolwiek jeszcze skompilowane przeciwko twojej klasie.
źródło
Chciałbym po prostu rzucić pomysł na adnotacje: @getter i @setter. Z @getter powinieneś mieć możliwość obj = class.field, ale nie class.field = obj. Z @setter i odwrotnie. Z @getter i @setter powinieneś być w stanie zrobić obie rzeczy. Pozwoliłoby to zachować enkapsulację i skrócić czas, nie wywołując prostych metod w czasie wykonywania.
źródło