Prywatne a chronione - problem dotyczący dobrych praktyk widoczności [zamknięte]


145

Szukałem i znam teoretyczną różnicę.

  • public - każda klasa / funkcja może uzyskać dostęp do metody / właściwości.
  • chroniony - tylko ta klasa i wszystkie podklasy mogą uzyskać dostęp do metody / właściwości.
  • private - tylko ta klasa może uzyskać dostęp do metody / właściwości. Nie zostanie nawet odziedziczona.

Wszystko w porządku, pytanie brzmi: jaka jest praktyczna różnica między nimi? Kiedy używałbyś privatei kiedy używałbyś protected? Czy istnieje standardowa lub akceptowalna dobra praktyka w stosunku do tej?

Do tej pory, aby zachować koncepcję dziedziczenia i polimorfizmu, używam publicdo wszystkiego, do czego można uzyskać dostęp z zewnątrz (jak konstruktory i funkcje głównych klas) oraz protecteddo metod wewnętrznych (logika, metody pomocnicze itp.). Czy jestem na dobrej drodze?

(Zauważ, że to pytanie jest dla mnie, ale także dla przyszłego odniesienia, ponieważ nie widziałem pytania takiego jak to SO).


33
Czy to ma znaczenie? Każdy język z obsługą OOP ma ten problem. Tak się składa, że ​​programuję w PHP, ale myślę, że pytanie dotyczy każdego języka obsługującego OOP.
Madara's Ghost

2
W porządku, po prostu zastanawiam się, czy zapomniałeś oznaczyć tagiem. Teraz widzę tag oop.
Paul Bellora

Czy muszę ustawić widoczność (prywatna / publiczna / chroniona) dla każdej właściwości klasy? A może tylko niektóre z nich muszą mieć typ widoczności? Jeśli tak, jak zdecydować, która nieruchomość musi mieć najwyższy poziom widoczności?
user4271704

1
z drugiej strony różnica między chronionym a prywatnym wydaje się oczywista. Użyj opcji protected, jeśli podklasy będą używać metody / zmiennej, w przeciwnym razie użyj private. W szczególności, jeśli podklasy musiałyby ponownie zdefiniować bardzo podobną zmienną prywatną w rodzicu, po prostu zabezpiecz ją.
iPherian,

Istnieje inny specyfikator dostępu internalw języku C #, który ogranicza poziom dostępu klasy lub jej członków w zestawie fizycznym. Chociaż nie jestem pewien, czy obsługuje je lub coś podobnego w innych językach.
RBT

Odpowiedzi:


128

Nie, nie jesteś na dobrej drodze. Praktyczna zasada brzmi: uczyń wszystko tak prywatnym, jak to tylko możliwe. To sprawia, że ​​twoja klasa jest bardziej hermetyzowana i pozwala na zmianę wewnętrznych elementów klasy bez wpływu na kod używający twojej klasy.

Jeśli zaprojektujesz klasę tak, aby była dziedziczona, ostrożnie wybierz to, co może być nadpisane i dostępne z podklas, i uczyń to chronionym (i na koniec, mówiąc o Javie, jeśli chcesz, aby była dostępna, ale nie można jej zastąpić). Należy jednak pamiętać, że gdy tylko zaakceptujesz posiadanie podklas swojej klasy i pojawi się chronione pole lub metoda, to pole lub metoda jest częścią publicznego interfejsu API klasy i nie można ich później zmienić bez zerwania podklas.

Klasa, która nie jest przeznaczona do dziedziczenia, powinna być ostateczna (w Javie). Możesz złagodzić niektóre reguły dostępu (od prywatnych do chronionych, od ostatecznych do nie ostatecznych) ze względu na testowanie jednostkowe, ale następnie udokumentuj to i wyjaśnij, że chociaż metoda jest chroniona, nie powinna być nadpisywana.


1
Prawdziwe pytanie tutaj jest o privatevs protectedKiedy chcę właściwością być dziedziczone, a kiedy nie? Naprawdę nie mogę stwierdzić, czy użytkownik czasami w przyszłości chce wziąć moją klasę i przedłużyć ją ...
Madara's Ghost

16
Cóż, nie chodzi o to, co użytkownik chce przesłonić, ale o to, co chcesz zezwolić na zastąpienie. Zwykle pomaga to zmienić strony i spróbować pomyśleć: Gdybym użył tej klasy i utworzył podklasę, co chciałbym mieć możliwość przesłonięcia? Czasami inni użytkownicy nadal będą tęsknić za
czymś

3
Pola chronione to zła praktyka w dziedziczeniu! . Proszę, strzeżcie się tego. Oto dlaczego
RBT

4
Prywatne pola są w praktyce złe w dziedziczeniu !. (przesadnie) Przeczytaj moją odpowiedź.
Larry

6
Typowa opinia maniaka kontroli.
bruno desthuilliers

58

Pozwólcie, że poprzedzę to stwierdzeniem, że mówię tutaj przede wszystkim o dostępie do metod oraz w nieco mniejszym stopniu o oznaczaniu klas jako końcowych, a nie dostępie do elementów członkowskich.

Stara mądrość

„oznacz to jako prywatne, chyba że masz dobry powód, aby tego nie robić”

miało sens w czasach, gdy zostało napisane, zanim open source zdominowało przestrzeń bibliotek programistów i zarządzanie VCS / zależnościami. stał się hiperwspółpracujący dzięki Githubowi, Mavenowi itp. W tamtych czasach można było również zarobić pieniądze, ograniczając sposób (-y) wykorzystania biblioteki. Prawdopodobnie pierwsze 8 lub 9 lat mojej kariery spędziłem na ścisłym przestrzeganiu tej „najlepszej praktyki”.

Dziś uważam, że to zła rada. Czasami istnieje rozsądny argument, aby oznaczyć metodę jako prywatną lub jako finał klasy, ale jest to niezwykle rzadkie, a nawet wtedy prawdopodobnie niczego nie poprawia.

Czy kiedykolwiek:

  • Czy byłeś rozczarowany, zaskoczony lub zraniony przez bibliotekę itp., Która miała błąd, który można było naprawić za pomocą dziedziczenia i kilku wierszy kodu, ale z powodu prywatnych / ostatecznych metod i klas byli zmuszeni czekać na oficjalną łatkę, która może nigdy nie nadejść? Mam.
  • Chciałeś użyć biblioteki do nieco innego przypadku użycia niż wyobrażali sobie autorzy, ale nie byli w stanie tego zrobić z powodu prywatnych / ostatecznych metod i klas? Mam.
  • Byłeś rozczarowany, zaskoczony lub zraniony przez bibliotekę itp., Która była zbyt liberalna w swojej rozszerzalności? Nie mam.

Oto trzy największe usprawiedliwienia, jakie słyszałem, dotyczące domyślnego oznaczania metod jako prywatnych:

Racjonalizacja nr 1: To niebezpieczne i nie ma powodu, aby zastępować określoną metodę

Nie mogę zliczyć, ile razy myliłem się co do tego, czy kiedykolwiek będzie potrzeba zastąpienia określonej metody, którą napisałem. Pracując nad kilkoma popularnymi bibliotekami open source, na własnej skórze poznałem prawdziwy koszt oznaczania rzeczy jako prywatnych. Często eliminuje jedyne praktyczne rozwiązanie nieprzewidzianych problemów lub przypadków użycia. I odwrotnie, nigdy od ponad 16 lat rozwoju zawodowego nie żałowałem oznaczenia metody chronionej zamiast prywatnej z powodów związanych z bezpieczeństwem API. Gdy programista decyduje się na rozszerzenie klasy i zastąpienie metody, świadomie mówi „Wiem, co robię”. a ze względu na produktywność powinno to wystarczyć. Kropka. Jeśli jest to niebezpieczne, zanotuj to w klasie / metodzie Javadocs, nie tylko na ślepo zatrzaskuj drzwi.

Metody znakowania chronione domyślnie są złagodzeniem dla jednego z głównych problemów współczesnego rozwoju oprogramowania: porażki wyobraźni.

Racjonalizacja nr 2: Utrzymuje publiczny interfejs API / Javadocs w czystości

Ten jest bardziej rozsądny iw zależności od grupy docelowej może być nawet właściwym rozwiązaniem, ale warto rozważyć, jaki jest koszt utrzymania „czystego” API: rozszerzalność. Z powodów wymienionych powyżej prawdopodobnie bardziej sensowne jest oznaczanie rzeczy chronionych domyślnie na wszelki wypadek.

Racjonalizacja # 3: Moje oprogramowanie jest komercyjne i muszę ograniczyć jego użycie.

Jest to również rozsądne, ale jako konsument wybrałbym za każdym razem mniej restrykcyjnego konkurenta (zakładając, że nie ma znaczących różnic w jakości).

Nigdy nie mów nigdy

Nie mówię, że nigdy nie oznaczaj metod jako prywatnych. Mówię, że lepszą praktyczną zasadą jest „ochrona metod, chyba że jest dobry powód, aby tego nie robić”.

Ta rada jest najbardziej odpowiednia dla osób pracujących nad bibliotekami lub projektami na większą skalę, które zostały podzielone na moduły. W przypadku mniejszych lub bardziej monolitycznych projektów nie ma to większego znaczenia, ponieważ i tak kontrolujesz cały kod i łatwo jest zmienić poziom dostępu do kodu, jeśli / kiedy go potrzebujesz. Mimo wszystko dałbym tę samą radę :-)


Napisz swoje Rationalization # 1- Kiedy oznaczam coś jako chronione, wybieram to wyraźnie, ponieważ czuję, że to zachowanie nie jest ostateczne i może być przypadkiem, w którym moje klasy podrzędne będą chciały je zastąpić. Jeśli nie jestem tego taki pewien, chciałbym domyślnie ustawić go jako prywatny. Szczególnie w języku takim jak C #, który domyślnie utrzymuje metodę niewirtualną, dodawanie samego specyfikatora dostępu chronionego nie ma sensu. Aby jasno określić cel, musisz dodać oba słowa kluczowe virtuali protectedsłowa kluczowe. Ponadto ludzie wolą skojarzenia od dziedziczenia, więc protecteddomyślnie trudno jest dostrzec
RBT,

4
Kombinacja słów kluczowych potrzebna do nadpisania metody to szczegółowy język IMHO. Kontrargument, który przedstawiam racjonalizacji nr 1, jest skierowany wprost do przypadku, o którym wspomniałeś "Jeśli nie jestem taki pewien ...". Praktycznie rzecz biorąc, jeśli nie możesz wymyślić powodu, dla którego byłoby to niebezpieczne, to więcej można zyskać, decydując się na rozszerzalność. Kiedy mówisz „skojarzenie”, traktuję to jako synonim kompozycji, w takim przypadku nie widzę w tym żadnego znaczenia.
Nick

3
Nie pamiętam, ile razy musiałem „klonować” podstawowe klasy tylko dlatego, że chciałem przesłonić 1 lub 2 lub metody. I tak jak powiedziałeś, w związku z trendem społecznym, że udostępniamy więcej kodu niż kiedykolwiek wcześniej, o wiele bardziej sensowne jest korzystanie z domyślnej ochrony niż prywatnej. tzn. bądź bardziej otwarty na własne logiki.
shinkou,

1
Zgodzić się. Chciałem tylko dostosować BufferedReader Javy, aby obsługiwał niestandardowe ograniczniki linii. Dzięki polom prywatnym muszę sklonować całą klasę zamiast po prostu ją rozszerzać i nadpisywać readLine ().
Ron Dunn

21

Przestańcie nadużywać prywatnych pól !!!

Komentarze tutaj wydają się w przeważającej mierze popierające korzystanie z pól prywatnych. Cóż, mam coś innego do powiedzenia.

Czy pola prywatne są w zasadzie dobre? Tak. Ale mówienie, że złotą zasadą jest uczynienie wszystkiego prywatnym, gdy nie jesteś pewien, jest zdecydowanie błędne ! Nie zobaczysz problemu, dopóki go nie napotkasz. Moim zdaniem powinieneś oznaczyć pola jako chronione, jeśli nie masz pewności .

Istnieją dwa przypadki, w których chcesz rozszerzyć klasę:

  • Chcesz dodać dodatkową funkcjonalność do klasy bazowej
  • Chcesz zmodyfikować istniejącą klasę, która jest poza bieżącym pakietem (być może w niektórych bibliotekach)

W pierwszym przypadku nie ma nic złego w polach prywatnych. Fakt, że ludzie nadużywają prywatnych pól, sprawia, że ​​frustrujące jest, gdy dowiadujesz się, że nie możesz modyfikować gówna.

Rozważ prostą bibliotekę modelującą samochody:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

Autor biblioteki pomyślał: nie ma powodu, dla którego użytkownicy mojej biblioteki muszą mieć dostęp do szczegółów implementacji, assembleCar()prawda? Oznaczmy śrubkę jako prywatną.

Cóż, autor się myli. Jeśli chcesz zmodyfikować tylko assembleCar()metodę bez kopiowania całej klasy do swojego pakietu, nie masz szczęścia. Musisz przepisać własne screwpole. Powiedzmy, że w tym samochodzie jest kilkanaście śrubek, a każda z nich zawiera jakiś nietrywialny kod inicjalizacyjny różnymi prywatnymi metodami, a wszystkie te śruby są oznaczone jako prywatne. W tym momencie zaczyna ssać.

Tak, możesz się ze mną spierać, że autor biblioteki mógł napisać lepszy kod, więc nie ma nic złego w polach prywatnych . Nie twierdzę, że pole prywatne jest problemem z OOP . Jest to problem, gdy ludzie ich używają.

Morał z tej historii jest taki, że jeśli piszesz bibliotekę, nigdy nie wiesz, czy Twoi użytkownicy chcą uzyskać dostęp do określonej dziedziny. Jeśli nie masz pewności, zaznacz to, protectedaby wszyscy byli później szczęśliwsi. Przynajmniej nie nadużywaj pola prywatnego .

Bardzo popieram odpowiedź Nicka.


2
Używanie pól prywatnych to głównie mówienie, że dziedziczenie jest złą abstrakcją, która zmienia określone zachowanie klasy / obiektu (jeśli jest używane świadomie). Zmiana zwykle po cichu narusza LSP, ponieważ zachowanie jest zmieniane bez zmiany interfejsu API. Świetna odpowiedź, +1.
Madara's Ghost

Kazanie! Prywatne to zmora mojego istnienia, kiedy próbuję pisać mody do Minecrafta. Nie ma nic bardziej irytującego niż konieczność użycia Reflection lub ASM, aby to naprawić.
RecursiveExceptionException

Jak zrozumiałem odpowiedź Nicka, odnosił się głównie do metod, a nie właściwości. Całkowicie się zgadzam, że nie powinniśmy za każdym razem deklarować metod jako prywatne, a ich użycie musi być przemyślane, ale powiedzmy, dlaczego miałbyś radzić deklarować właściwości chronione zamiast prywatnych tylko dlatego, że chciałbyś łatwiej „przepisać” klasę. Całkowicie podziel się swoją frustracją, ponieważ codziennie pracuję z 3rd p, ale zachęcajmy ludzi do tworzenia solidnej architektury, a nie tylko mówienia: „kod skończyłby się bzdurą, więc zabezpieczmy się od początku, abyśmy mogli go łatwo przepisać”. Chociaż podzielam twoją frustrację.
sean662

16

Niedawno przeczytałem artykuł, w którym mówiono o blokowaniu wszystkich zajęć tak często, jak to możliwe. Spraw, aby wszystko było ostateczne i prywatne, chyba że masz natychmiastową potrzebę ujawnienia niektórych danych lub funkcji światu zewnętrznemu. Zawsze łatwo jest rozszerzyć zakres, aby później było bardziej dopuszczalne, ale nie na odwrót. Najpierw rozważ zrobienie jak największej liczby rzeczyfinal , które uczynią wyboru pomiędzy privatei protectedznacznie łatwiejsze.

  1. Dopilnuj, aby wszystkie klasy były ostateczne, chyba że musisz od razu przejść do podklasy.
  2. Spraw, aby wszystkie metody były ostateczne, chyba że musisz od razu przejść do podklasy i zastąpić je.
  3. Spraw, aby wszystkie parametry metody były ostateczne, chyba że musisz je zmienić w treści metody, co i tak jest dość niewygodne w większości przypadków.

Teraz, jeśli zostanie ci ostatnia klasa, ustaw wszystko jako prywatne, chyba że coś jest absolutnie potrzebne światu - upublicznij to.

Jeśli zostanie ci klasa, która ma podklasy, dokładnie zbadaj każdą właściwość i metodę. Najpierw zastanów się, czy chcesz nawet ujawnić tę właściwość / metodę podklasom. Jeśli tak, zastanów się, czy podklasa może siać spustoszenie w obiekcie, jeśli zepsuła wartość właściwości lub implementację metody w procesie przesłonięcia. Jeśli to możliwe i chcesz chronić właściwość / metodę swojej klasy nawet przed podklasami (wiem, brzmi to ironicznie), uczyń ją prywatną. W przeciwnym razie zabezpiecz go.

Zastrzeżenie: nie programuję zbyt wiele w Javie :)


2
Dla wyjaśnienia: A final classw Javie to klasa, z której nie można dziedziczyć. A final methodnie jest wirtualny, tj. Nie można go zastąpić przez podklasę (metody Java są domyślnie wirtualne). A final field/parameter/variablejest niezmiennym odwołaniem do zmiennej (w tym sensie, że nie można go zmodyfikować po zainicjowaniu).
M.Stramm,

1
Co zabawne, widziałem dokładnie odwrotną radę, że nic nie powinno być ostateczne, chyba że jest pewne, że nadpisanie danej rzeczy może potencjalnie złamać niezmiennik. W ten sposób każdy może dowolnie rozszerzać Twoje zajęcia.
GordonM

Czy możesz wymienić jeden przypadek w swojej karierze programisty, w którym oznaczenie parametru metody jako „ostateczne” miało wpływ na Twoje zrozumienie? (inaczej niż jest to wymagane przez kompilator)
user949300

7

Kiedy używałbyś privatei kiedy używałbyś protected?

Prywatne dziedziczenie można traktować jako implementowane w kategoriach relacji, a nie relacji IS-A . Mówiąc najprościej, zewnętrzny interfejs klasy dziedziczącej nie ma (widocznego) związku z odziedziczoną klasą, wykorzystuje privatedziedziczenie tylko do zaimplementowania podobnej funkcjonalności, jaką zapewnia klasa Base.

W przeciwieństwie do dziedziczenia prywatnego, dziedziczenie chronione jest ograniczoną formą dziedziczenia, w której klasa pochodna IS-A jest rodzajem klasy bazowej i chce ograniczyć dostęp członków pochodnych tylko do klasy pochodnej.


2
To „Wdrożone w kategoriach relacji” jest relacją HAS-A. Jeśli wszystkie atrybuty są prywatne, jedynym sposobem na uzyskanie do nich dostępu są metody. To jest to samo, co gdyby podklasa zawierała obiekt nadklasy. Wyeliminowanie wszystkich chronionych atrybutów z konkretnych klas oznacza również wyeliminowanie z nich całego dziedziczenia.
shawnhcorey

2
Ciekawy proces myślowy - Dziedziczenie prywatne . @shawnhcorey dziękuje za wyraźne wskazanie, że wskazuje na relację HAS-A, czyli skojarzenie (które może być agregacją lub kompozycją). Uważam, że ten post również powinien zostać zaktualizowany, aby wyjaśnić to samo.
RBT

1

Cóż, chodzi o hermetyzację, jeśli klasy paybill obsługują fakturowanie płatności, a następnie w klasie produktu, dlaczego miałby on potrzebować całego procesu fakturowania, tj. Metody płatności, jak zapłacić, gdzie zapłacić ... więc tylko zezwalając na to, co jest używane dla innych klas i obiektów nic poza tym publicznym dla tych, których inne klasy również używałyby, chronionym dla tych limitów tylko dla rozszerzających klas. Ponieważ jesteś madara uchiha, prywatny jest tak "limboo", jak widzisz (prowadzisz tylko jedną klasę).

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.