Czysty kod sugeruje unikanie chronionych zmiennych w sekcji „Odległość pionowa” rozdziału „Formatowanie”:
Pojęcia ściśle ze sobą powiązane powinny być trzymane pionowo blisko siebie. Oczywiście ta reguła nie działa w przypadku pojęć należących do oddzielnych plików. Ale ściśle powiązane pojęcia nie powinny być dzielone na różne pliki, chyba że masz bardzo dobry powód. Rzeczywiście jest to jeden z powodów, dla których należy unikać chronionych zmiennych .
Jakie jest uzasadnienie?
code-quality
clean-code
variables
Matsemann
źródło
źródło
Odpowiedzi:
Chronionych zmiennych należy unikać, ponieważ:
Ale jak widzisz, wszystkie z nich są „skłonne”. Czasami chroniony członek jest najbardziej eleganckim rozwiązaniem. Funkcje chronione mają zwykle mniej tych problemów. Ale istnieje wiele rzeczy, które powodują, że traktuje się ich ostrożnie. Dzięki wszystkim, co wymaga tego rodzaju opieki, ludzie popełniają błędy, aw świecie programowania oznacza to błędy i problemy projektowe.
źródło
To naprawdę ten sam powód, dla którego unikasz globalizacji, tylko na mniejszą skalę. Jest tak, ponieważ trudno jest znaleźć wszędzie, gdzie odczytywana jest zmienna lub, co gorsza, jest zapisywana i trudno jest zapewnić spójność użycia. Jeśli użycie jest ograniczone, na przykład pisanie w jednym oczywistym miejscu w klasie pochodnej i czytanie w jednym oczywistym miejscu w klasie bazowej, wówczas zmienne chronione mogą uczynić kod bardziej przejrzystym i zwięzłym. Jeśli masz ochotę czytać i pisać zmienną nie chcąc w wielu plikach, lepiej ją zamknąć.
źródło
Nie przeczytałem tej książki, ale mogę sprawdzić, co miał na myśli wujek Bob.
Jeśli coś włożysz
protected
, oznacza to, że klasa może to odziedziczyć. Ale zmienne składowe powinny należeć do klasy, w której są zawarte; jest to część podstawowej enkapsulacji. Zastosowanieprotected
zmiennej składowej przerywa enkapsulację, ponieważ teraz klasa pochodna ma dostęp do szczegółów implementacji klasy podstawowej. To ten sam problem, który pojawia się, gdy tworzysz zmiennąpublic
w zwykłej klasie.Aby rozwiązać problem, możesz umieścić zmienną w chronionej właściwości, na przykład:
Pozwala to
name
na bezpieczne ustawienie z klasy pochodnej za pomocą argumentu konstruktora, bez ujawniania szczegółów implementacji klasy podstawowej.źródło
protected
ipublic
członkami. JeśliBaseType
ujawnia członka publicznego, oznacza to, że wszystkie typy pochodne muszą mieć ten sam element działający w ten sam sposób, ponieważDerivedType
instancja może zostać przekazana do kodu, który odbieraBaseType
odwołanie i spodziewa się użyć tego elementu. Natomiast jeśliBaseType
ujawniaprotected
członka,DerivedType
może oczekiwać dostępu do tego członkabase
, alebase
nie może być niczym innym jakBaseType
.Argument wujka Boba dotyczy przede wszystkim odległości: jeśli masz pojęcie ważne dla klasy, umieść je razem z klasą w tym pliku. Nie rozdzielone między dwa pliki na dysku.
Chronione zmienne składowe są rozproszone w dwóch miejscach i wygląda to trochę jak magia. Odwołujesz się do tej zmiennej, ale nie jest tu zdefiniowana ... gdzie ona jest zdefiniowana? I tak zaczyna się polowanie. Lepiej
protected
całkowicie unikać jego argumentacji.Teraz nie wierzę, że regułą należy przestrzegać litery cały czas (na przykład: nie będziesz używać
protected
). Spójrz na ducha tego, do czego zmierza ... połącz powiązane ze sobą rzeczy w jeden plik - użyj do tego technik programowania i funkcji. Radziłbym, abyś nie analizował nadmiernie i nie wplątał się w szczegóły na ten temat.źródło
Kilka bardzo dobrych odpowiedzi już tutaj. Ale jedną rzeczą do dodania:
W większości współczesnych języków OO dobrym nawykiem (w Javie AFAIK jest to konieczne), aby umieścić każdą klasę we własnym pliku (proszę nie zwracać uwagi na C ++, gdzie często masz dwa pliki).
Jest więc praktycznie niemożliwe, aby funkcje w klasie pochodnej uzyskiwały dostęp do chronionej zmiennej klasy bazowej „pionowo” w kodzie źródłowym do definicji zmiennej. Ale idealnie powinny być tam przechowywane, ponieważ chronione zmienne są przeznaczone do użytku wewnętrznego, co sprawia, że funkcje uzyskujące do nich dostęp często są „ściśle powiązaną koncepcją”.
źródło
Podstawową ideą jest to, że „pole” (zmienna na poziomie instancji) zadeklarowane jako chronione jest prawdopodobnie bardziej widoczne niż powinno być i mniej „chronione”, niż byś chciał. W C / C ++ / Java / C # nie ma modyfikatora dostępu, który jest odpowiednikiem „dostępnego tylko dla klas podrzędnych w tym samym zestawie”, co daje możliwość definiowania własnych dzieci, które mogą uzyskać dostęp do pola w zestawie, ale nie zezwalanie dzieciom utworzonym w innych zespołach na taki sam dostęp; C # ma modyfikatory wewnętrzne i chronione, ale ich połączenie sprawia, że dostęp jest „wewnętrzny lub chroniony”, a nie „wewnętrzny i chroniony”. Tak więc chronione pole jest dostępne dla każdego dziecka, niezależnie od tego, czy napisałeś to dziecko, czy ktoś inny. Chronione są zatem otwarte drzwi do hakera.
Ponadto pola z ich definicji praktycznie nie mają walidacji związanej z ich zmianą. w C # możesz zrobić tylko do odczytu, co sprawia, że typy wartości są faktycznie stałe, a typy referencyjne nie mogą być ponownie zainicjowane (ale nadal bardzo zmienne), ale o to chodzi. Jako takie, nawet chronione, twoje dzieci (którym nie możesz ufać) mają dostęp do tego pola i mogą ustawić je na coś nieprawidłowego, co powoduje, że stan obiektu jest niespójny (czego należy unikać).
Akceptowanym sposobem pracy z polami jest uczynienie ich prywatnymi i dostęp do nich za pomocą właściwości i / lub metody pobierającej i ustawiającej. Jeśli wszyscy konsumenci klasy potrzebują tej wartości, upublicznij getter (przynajmniej). Jeśli potrzebują tego tylko dzieci, zapewnij getterowi ochronę.
Innym podejściem, które odpowiada na pytanie, jest postawienie sobie pytania; dlaczego kod w metodzie podrzędnej wymaga możliwości bezpośredniej modyfikacji moich danych stanu? Co to mówi o tym kodzie? To argument „pionowej odległości” na jego twarzy. Jeśli w dziecku jest kod, który musi bezpośrednio zmieniać stan rodzica, to może ten kod powinien należeć do rodzica?
źródło
To ciekawa dyskusja.
Szczerze mówiąc, dobre narzędzia mogą złagodzić wiele z tych „pionowych” problemów. W rzeczywistości moim zdaniem pojęcie „pliku” w rzeczywistości utrudnia tworzenie oprogramowania - nad czym pracuje projekt Light Table (http://www.chris-granger.com/2012/04/12/light- table --- a-new-ide-concept /).
Usunąć pliki ze zdjęcia, a chroniony zakres staje się znacznie bardziej atrakcyjny. Chroniona koncepcja staje się najbardziej wyraźna w językach, takich jak SmallTalk - gdzie każde dziedzictwo po prostu rozszerza oryginalną koncepcję klasy. Powinien robić wszystko i mieć wszystko, co klasa macierzysta.
Moim zdaniem hierarchie klas powinny być tak płytkie, jak to możliwe, przy czym większość rozszerzeń pochodzi z kompozycji. W takich modelach nie widzę żadnych powodów, dla których zmienne prywatne nie powinny być chronione. Jeśli klasa rozszerzona ma reprezentować rozszerzenie obejmujące wszystkie zachowania klasy nadrzędnej (co moim zdaniem powinno, a zatem uważam, że metody prywatne są z natury złe), dlaczego klasa rozszerzona nie powinna reprezentować również przechowywania takich danych?
Innymi słowy - w każdym przypadku, w którym uważam, że zmienna prywatna byłaby lepsza niż chroniona, dziedziczenie nie jest rozwiązaniem problemu.
źródło
Jak tam napisano, jest to tylko jeden z powodów, dla których logicznie połączony kod powinien być umieszczony w fizycznie połączonych jednostkach (plikach, pakietach i innych). W mniejszej skali jest to ten sam powód, dla którego nie umieścisz klasy, która tworzy zapytania DB w pakiecie z klasami wyświetlającymi interfejs użytkownika.
Głównym powodem, dla którego chronione zmienne nie są zalecane, jest to, że mają tendencję do przerywania enkapsulacji. Zmienne i metody powinny mieć możliwie ograniczoną widoczność; w celu uzyskania dalszych informacji patrz Efektywna Java Joshua Blocha , punkt 13 - „Minimalizuj dostępność klas i członków”.
Jednak nie powinieneś brać całej tej reklamy, tylko jako linii cechu; jeśli chronione zmienne są bardzo złe, nie zostałyby umieszczone w języku. Rozsądnym miejscem dla chronionych pól jest imho w klasie bazowej z frameworka testowego (klasy testów integracyjnych go rozszerzają).
źródło
Uważam, że używanie chronionych zmiennych do testów JUnit może być przydatne. Jeśli ustawisz je jako prywatne, nie będziesz mieć do nich dostępu podczas testów bez refleksji. Może to być przydatne, jeśli testujesz złożony proces poprzez wiele zmian stanu.
Możesz ustawić je jako prywatne za pomocą chronionych metod pobierających, ale jeśli zmienne będą używane tylko zewnętrznie podczas testu JUnit, nie jestem pewien, czy to lepsze rozwiązanie.
źródło
Tak, zgadzam się, że jest to nieco dziwne, biorąc pod uwagę, że (jak pamiętam) książka omawia również wszystkie nieprywatne zmienne jako coś, czego należy unikać.
Myślę, że problem z chronionym modyfikatorem polega na tym, że członek jest nie tylko narażony na podklasy, ale także jest widoczny dla całego pakietu. Myślę, że to ten podwójny aspekt, w którym wujek Bob bierze tutaj wyjątek. Oczywiście nie zawsze można ominąć chronione metody, a tym samym kwalifikację chronionej zmiennej.
Myślę, że bardziej interesującym podejściem jest upublicznienie wszystkiego, co zmusi cię do posiadania małych klas, co z kolei sprawi, że cała metryka odległości w pionie będzie nieco dyskusyjna.
źródło