Powody, dla których warto używać pól i metod prywatnych zamiast chronionych

To dość podstawowe pytanie OO, ale dręczące mnie od jakiegoś czasu.

Zazwyczaj unikam używania modyfikatora widoczności "prywatnej" dla moich pól i metod na rzecz protected.

Dzieje się tak, ponieważ, ogólnie rzecz biorąc, nie widzę żadnego zastosowania w ukrywaniu implementacji między klasą bazową a klasą potomną, z wyjątkiem sytuacji, gdy chcę ustawić konkretne wytyczne dla rozszerzenia moich klas (np. w frameworkach). W większości przypadków myślę, że staram się ograniczyć jak moja klasa będzie Rozszerzony przeze mnie lub przez innych użytkowników nie jest korzystny.

Ale, dla większości ludzi, modyfikator private jest zwykle domyślnym wyborem podczas definiowania Niepublicznego pola/metody.

Czy możesz wymienić przypadki użycia dla private? Czy istnieje poważny powód, dla którego zawsze używasz prywatnego? Czy też uważasz, że jest nadużywane?
Author: Keith Pinson, 2011-02-06

7 answers

Istnieje konsensus, że należy preferować kompozycję nad dziedziczeniem w OOP. Istnieje kilka powodów tego (google, jeśli jesteś zainteresowany), ale główną częścią jest to, że: {]}

    Dziedziczenie rzadko jest najlepszym narzędziem i nie jest tak elastyczne jak inne rozwiązania]}
  • protected members / fields form a interface towards your subclasses
  • interfejsy (i założenia dotyczące ich przyszłego wykorzystania) są trudne do poprawienia i udokumentowania properly

Dlatego, jeśli zdecydujesz się na dziedziczenie klasy, powinieneś to zrobić concierge i ze wszystkimi zaletami i wadami na uwadze.

Dlatego lepiej nie dziedziczyć klasy i zamiast tego upewnić się, że jest ona tak elastyczna, jak to tylko możliwe (i nie więcej) za pomocą innych środków.

Jest to głównie oczywiste w większych frameworkach, gdzie użycie twojej klasy jest poza Twoją kontrolą. Dla własnej małej aplikacji nie zauważysz tego tak bardzo , ale to (dziedziczenie domyślnie) ugryzie cię w plecy prędzej czy później, jeśli nie będziesz ostrożny.

Alternatywy

Kompozycja oznacza, że można ujawnić możliwość dostosowania za pomocą jawnych (w pełni abstrakcyjnych) interfejsów (wirtualnych lub opartych na szablonach).

Więc zamiast mieć klasę bazową pojazdu z funkcją virtual drive () (wraz ze wszystkim innym, np. liczbą całkowitą dla ceny itp.), miałbyś klasę pojazdu biorąc obiekt interfejsu silnika, a ten silnik interfejs wyświetla tylko funkcję drive (). Teraz możesz dodawać i ponownie używać dowolnego silnika w dowolnym miejscu (mniej więcej. :).

 29
Author: Macke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:51

Istnieją dwie sytuacje, w których ważne jest, czy członek jest protected LUB private:

  1. jeśli klasa pochodna mogłaby skorzystać z używania członka, uczynienie go "chronionym" pozwoliłoby mu na to, podczas gdy uczynienie go "prywatnym" odmówiłoby mu tej korzyści.
  2. jeśli przyszła wersja klasy bazowej mogłaby skorzystać z tego, że członek nie zachowuje się tak, jak ma to miejsce w obecnej wersji, uczynienie członka "prywatnym" pozwoliłoby tej przyszłej wersji na zmianę zachowania (lub wyeliminowanie członek w całości), podczas gdy uczynienie go "chronionym" wymagałoby od wszystkich przyszłych wersji klasy zachowania tego samego, odmawiając im w ten sposób korzyści, które można by czerpać ze zmiany.

Jeśli można sobie wyobrazić realistyczny scenariusz, w którym Klasa pochodna może odnieść korzyści z dostępu do członka i nie wyobraża sobie scenariusza, w którym klasa bazowa może odnieść korzyści ze zmiany jego zachowania, to członkiem powinno być protected [zakładając, oczywiście, że nie powinno być publiczne]. Jeśli nie można sobie wyobrazić scenariusza, w którym Klasa pochodna miałaby wiele korzyści z bezpośredniego dostępu do członka, ale można sobie wyobrazić scenariusze, w których przyszła wersja klasy bazowej mogłaby skorzystać zmieniając go, to powinno to być private. Te sprawy są dość jasne i proste.

Jeśli nie ma żadnego wiarygodnego scenariusza, w którym klasa bazowa skorzystałaby na zmianie członka, sugerowałbym, aby pochylić się nad jego stworzeniem protected. Niektórzy powiedzieliby, że "YAGNI "(You Ain ' t Gonna Need It) Jeśli oczekujesz, że inni odziedziczą klasę, uczynienie członka prywatnym nie zakłada "YAGNI", ale raczej "HAGNI" (nie będzie tego potrzebował). O ile "ty" nie będzie musiał zmienić zachowania przedmiotu w przyszłej wersji klasy," ty " nie będzie musiał być private. Natomiast w wielu przypadkach nie będziesz miał możliwości przewidywania, czego mogą potrzebować konsumenci twojej klasy. To nie znaczy, że należy członkowie protected bez uprzedniej próby określenia, w jaki sposób można odnieść korzyści z ich zmiany, ponieważ YAGNI tak naprawdę nie ma zastosowania do żadnej z tych decyzji. YAGNI stosuje się w przypadkach, w których możliwe będzie poradzenie sobie z przyszłą potrzebą, jeśli i kiedy zostanie ona napotkana, więc nie ma potrzeby radzenia sobie z nią teraz. Decyzja, aby członek klasy, która jest przekazywana innym programistom private lub protected, oznacza decyzję, jaki rodzaj potencjalnych przyszłych potrzeb będzie przewidziany, i utrudni zadbaj o drugiego.

Czasami oba scenariusze będą wiarygodne, w którym to przypadku pomocne może być zaoferowanie dwóch klas-z których jedna ujawnia dane elementy i klasa wywodząca się z tego, co nie (nie ma standardowego idiomatycznego, aby Klasa pochodna ukrywała elementy odziedziczone po rodzicu, chociaż deklarowanie nowych elementów, które mają te same nazwy, ale nie są kompilowalne i są oznaczone atrybutem Obsolete, miałoby taki skutek). Jako przykład kompromisy, rozważmy List<T>. Jeśli Typ wyświetla tablicę pomocniczą jako chroniony element, można zdefiniować typ Pochodny CompareExchangeableList<T> where T:Class, który zawiera element T CompareExchangeItem(index, T T newValue, T oldvalue), który zwróci Interlocked.CompareExchange(_backingArray[index], newValue, oldValue); taki typ może być użyty przez dowolny kod, który oczekiwał List<T>, ale kod, który wiedział, że instancja jest CompareExchangeableList<T>, może użyć CompareExchangeItem na nim. Niestety, ponieważ List<T> nie udostępnia tablicy pomocniczej klasom pochodnym, niemożliwe jest zdefiniowanie typu, który pozwala CompareExchange na liście elementy, ale które nadal byłyby użyte przez kod oczekujący List<T>.

Pomimo tego, że wszystkie istniejące implementacje List<T> używają jednej tablicy, Microsoft może zaimplementować przyszłe wersje, aby używać wielu tablic, gdy lista w przeciwnym razie wzrośnie poza 84K, tak aby uniknąć nieefektywności związanych z dużą stertą obiektów. Jeśli tablica nośna została odsłonięta jako element chroniony, to wprowadzenie takiej zmiany byłoby niemożliwe bez złamania jakiegokolwiek kodu, który opierał się na tym członku.

Właściwie, idealną rzeczą mogło być zrównoważenie tych interesów przez zapewnienie chronionego członka, który, biorąc pod uwagę indeks pozycji listy, zwróci segment tablicy zawierający wskazany element. Jeśli jest tylko jedna tablica, metoda zawsze zwraca odniesienie do tej tablicy, z przesunięciem zera, początkowy indeks dolny zera i długością równą długości listy. Jeśli a przyszĹ 'a wersja List<T> podzieliĺ' a tablicÄ ™ na wiele czÄ ™ Ĺ "ci, metoda ta moĹźe pozwoliÄ ‡ klasom pochodnym na efektywny dostÄ ™ p do segmentów tablicy w sposĂłb, ktĂłry nie byĹ' by moĹźliwy bez takiego dostÄ ™ pu [np. uĹźywajÄ ... C Array.Copy], Ale List<T> moĹźe zmieniÄ ‡ sposĂłb zarzÄ ... dzania swoim zapasowym magazynem bez rozbijania poprawnie napisanych klas pochodnych. Niewłaściwie napisane klasy pochodne mogą zostać uszkodzone, jeśli implementacja bazowa ulegnie zmianie, ale jest to wina klasy pochodnej, a nie bazy.

 15
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 19:23:55

Po prostu wolę prywatne niż chronione w przypadku domyślnym, ponieważ kieruję się zasadą, aby ukryć jak najwięcej i dlatego ustawić widoczność tak nisko, jak to możliwe.

 4
Author: RoflcoptrException,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-06 11:38:46

Sięgam tutaj. Uważam jednak, że używanie chronionych zmiennych członkowskich powinno być wykonywane concierge, ponieważ nie tylko planujesz dziedziczyć, ale także dlatego, że istnieje solidny powód, dla którego derived nie powinien używać właściwości Setters/Getters zdefiniowanych NA klasie bazowej.

W OOP "enkapsulujemy" pola członkowskie, dzięki czemu możemy kontrolować, w jaki sposób właściwości reprezentowania są dostępne i zmieniane. Kiedy definiujemy getter/setter w naszej bazie dla zmiennej członkowskiej, zasadniczo mówimy, że w ten sposób chcę, aby ta zmienna była odwoływana / używana.

Chociaż istnieją wyjątki projektowe, w których może być konieczna zmiana zachowania utworzonego w metodach getter/setter klasy bazowej, wydaje mi się, że byłaby to decyzja podjęta po uważnym rozważeniu alternatyw.

Na przykład, kiedy muszę uzyskać dostęp do pola member bezpośrednio z klasy pochodnej, zamiast przez getter/setter, zaczynam myśleć, że może ta szczególna właściwość powinna być zdefiniowana jako abstrakcyjna, a nawet przeniesiona do klasy pochodnej. Zależy to od tego, jak szeroka jest hierarchia i jak wiele dodatkowych rozważań. Ale dla mnie, chodzenie po własności publicznej zdefiniowanej na klasie podstawowej zaczyna pachnieć.

Oczywiście, w wielu przypadkach "nie ma to znaczenia", ponieważ nie implementujemy niczego wewnątrz gettera / settera poza dostępem do zmiennej. Ale znowu, jeśli tak jest, klasa pochodna może równie łatwo dostęp przez getter/setter. Chroni to również przed trudnymi do znalezienia później błędami, jeśli są stosowane konsekwentnie. Jeśli behgavior gettera / settera dla pola członka w klasie bazowej zostanie w jakiś sposób zmieniony, a klasa pochodna odwołuje się bezpośrednio do chronionego pola, istnieje możliwość wystąpienia problemów.

 2
Author: XIVSolutions,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-06 18:44:30

Jesteś na dobrej drodze. Robisz coś prywatnego, ponieważ twoja implementacja zależy od tego, czy nie zostanie zmieniona ani przez użytkownika, ani potomka.

Domyślam się, że prywatny, a następnie podejmuję świadomą decyzję o tym, czy i ile wewnętrznych działań zamierzam zdemaskować, wydajesz się działać na podstawie tego, że i tak zostanie zdemaskowany, więc do roboty. Tak długo, jak pamiętamy, by skrzyżować wszystkie oczy i kropki na Trójniki, jesteśmy dobrzy.

Another way to look at to jest to. Jeśli uczynisz to prywatnym, ktoś może nie być w stanie zrobić tego, co chce z Twoją implementacją.

Jeśli nie zrobisz tego prywatnie, ktoś może być w stanie zrobić coś, czego naprawdę nie chcesz, aby zrobił z Twoją implementacją.

 1
Author: Tony Hopkinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 18:25:39

Programuję OOP od C++ w 1993 roku i Java w 1995 roku. Raz po raz widziałem potrzebę powiększenia lub zmiany klasy, zazwyczaj dodając dodatkową funkcjonalność ściśle zintegrowaną z klasą. Sposobem OOP jest podklasowanie klasy bazowej i dokonanie zmian w podklasie. Na przykład pole klasy bazowej, o którym mowa pierwotnie tylko w innym miejscu klasy bazowej, jest potrzebne do innej akcji lub innej aktywności, która musi zmienić wartość pola (lub jednego z pól członków). Jeśli to pole jest prywatne w klasie bazowej, to podklasa nie może uzyskać do niego dostępu, nie może rozszerzyć funkcjonalności. Jeśli pole jest chronione, może to zrobić.

Podklasy mają specjalną relację do klasy bazowej, której inne klasy w hierarchii klas nie mają: dziedziczą Członkowie klasy bazowej. Celem dziedziczenia jest dostęp do członków klasy bazowej; dziedziczenie prywatne. Skąd programista klasy bazowej ma wiedzieć, że nie ma podklas czy kiedykolwiek będziesz potrzebował dostępu do członka? W niektórych przypadkach może to być jasne, ale prywatne powinny być wyjątkiem, a nie regułą. Deweloperzy subklasujący klasę bazową mają kod źródłowy klasy bazowej, więc ich alternatywą jest bezpośrednia zmiana klasy bazowej (być może tylko zmiana statusu prywatnego na chroniony przed subklasą). To nie jest czyste, dobra praktyka, ale to jest to, co prywatne sprawia, że robisz.

 1
Author: Matthew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-26 16:51:22

Jestem początkującym w OOP, ale jestem już od pierwszych artykułów w ACM i IEEE. Z tego, co pamiętam, ten styl rozwoju był bardziej do modelowania czegoś. W realnym świecie rzeczy, w tym procesy i operacje, miałyby elementy "prywatne, chronione i publiczne". Więc być wiernym przedmiotowi .....

Poza modelowaniem czegoś, programowanie polega bardziej na rozwiązywaniu problemu. Kwestia elementów "prywatnych, chronionych i publicznych" jest problemem tylko wtedy, gdy odnosi się do tworzenia niezawodnego rozwiązania. Jako rozwiązujący problemy, nie popełniłbym błędu coraz kaszel w jak inni używają mojego rozwiązania do rozwiązywania własnych problemów. Teraz należy pamiętać, że głównym powodem problemu ...., było umożliwienie miejsca do sprawdzania danych (tj. sprawdzania danych w prawidłowym zakresie i strukturze przed użyciem ich w obiekcie).

Mając to na uwadze, jeśli twój kod rozwiązuje problem, do którego został zaprojektowany, wykonałeś swoją pracę. Jeśli inni potrzebują Twojego rozwiązanie, aby rozwiązać ten sam lub symulowany problem - cóż, czy naprawdę musisz kontrolować, jak to robią. Powiedziałbym: "tylko wtedy, gdy czerpiesz z tego jakieś korzyści lub znasz słabości swojego projektu, więc musisz chronić niektóre rzeczy."

 0
Author: Charles DaCosta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-14 09:17:11