Czy należy kiedykolwiek używać chronionych zmiennych składowych? Jakie są zalety i jakie problemy może to powodować?
Czy należy kiedykolwiek używać chronionych zmiennych składowych? Jakie są zalety i jakie problemy może to powodować?
Odpowiedzi:
Czy należy kiedykolwiek używać chronionych zmiennych składowych?
Zależy od tego, jak bardzo jesteś wybredny w kwestii ukrywania się.
Jeśli pojawi się programista i podklasuje twoją klasę, może to zepsuć, ponieważ nie zrozumie jej w pełni. W przypadku członków prywatnych, innych niż interfejs publiczny, nie mogą zobaczyć szczegółów implementacji dotyczących sposobu wykonywania czynności, co zapewnia elastyczność późniejszej zmiany.
Obecnie ogólne odczucie jest takie, że powodują one nadmierne sprzężenie między klasami pochodnymi a ich bazami.
Nie mają szczególnej przewagi nad chronionymi metodami / właściwościami (dawno temu mogły mieć niewielką przewagę wydajnościową), a także były bardziej używane w czasach, gdy modne było bardzo głębokie dziedziczenie, czego obecnie nie ma.
no particular advantage over protected methods/properties
być no particular advantage over *private* methods/properties
?
super
konstrukcji do wywołania konstruktora nadrzędnego; zajmie się wtedy inicjalizacją prywatnych zmiennych stanu w klasie nadrzędnej.
Ogólnie rzecz biorąc, jeśli coś nie jest celowo pomyślane jako publiczne, ustawiam to jako prywatne.
Jeśli zajdzie sytuacja, w której potrzebuję dostępu do tej prywatnej zmiennej lub metody z klasy pochodnej, zmieniam ją z prywatnej na chronioną.
To rzadko się zdarza - naprawdę nie jestem fanem dziedziczenia, ponieważ nie jest to szczególnie dobry sposób na modelowanie większości sytuacji. W każdym razie kontynuuj, bez obaw.
Powiedziałbym, że jest to w porządku (i prawdopodobnie najlepszy sposób na zrobienie tego) dla większości programistów.
Prosty fakt jest taki , że jeśli jakiś inny programista pojawi się rok później i zdecyduje, że potrzebuje dostępu do twojej prywatnej zmiennej członkowskiej, po prostu zamierzają edytować kod, zmienić go na chroniony i kontynuować swoją działalność.
Jedynymi prawdziwymi wyjątkami od tej reguły są sytuacje, w których prowadzisz działalność polegającą na wysyłaniu binarnych bibliotek dll w formie czarnej skrzynki do stron trzecich. Składa się w zasadzie z Microsoftu, dostawców „Custom DataGrid Control” i może kilku innych dużych aplikacji, które są dostarczane z bibliotekami rozszerzalności. Jeśli nie należysz do tej kategorii, nie warto tracić czasu / wysiłku, aby martwić się o tego typu rzeczy.
Kluczową kwestią dla mnie jest to, że po utworzeniu zmiennej chronionej nie możesz pozwolić żadnej metodzie w Twojej klasie na poleganie na jej wartości w zakresie, ponieważ podklasa zawsze może umieścić ją poza zakresem.
Na przykład, jeśli mam klasę, która definiuje szerokość i wysokość renderowanego obiektu i chronię te zmienne, nie mogę wtedy przyjmować żadnych założeń dotyczących (na przykład) proporcji.
Co najważniejsze, nigdy nie mogę przyjąć tych założeń w żadnym momencie od momentu wydania kodu jako biblioteki, ponieważ nawet jeśli zaktualizuję moje setery, aby zachować proporcje, nie mam gwarancji, że zmienne są ustawiane przez setery lub dostępne za pośrednictwem metody pobierające w istniejącym kodzie.
Żadna podklasa z mojej klasy nie może też zdecydować się na taką gwarancję, ponieważ nie może również wymusić wartości zmiennych, nawet jeśli jest to cały punkt ich podklasy .
Jako przykład:
Ograniczając zmienne, aby były prywatne, mogę wymusić zachowanie, które chcę, za pomocą metod ustawiających lub pobierających.
Ogólnie rzecz biorąc, chciałbym zachować chronione zmienne członkowskie w rzadkich przypadkach, w których masz całkowitą kontrolę nad kodem, który również ich używa. Jeśli tworzysz publiczny interfejs API, powiedziałbym, że nigdy. Poniżej będziemy odnosić się do zmiennej składowej jako do „właściwości” obiektu.
Oto, czego twoja nadklasa nie może zrobić po ustawieniu zmiennej składowej chronionej, a nie prywatnej z dostępami:
leniwie twórz wartości w locie, gdy właściwość jest odczytywana. Jeśli dodasz chronioną metodę pobierania, możesz leniwie utworzyć wartość i przekazać ją z powrotem.
wiedzieć, kiedy właściwość została zmodyfikowana lub usunięta. Może to powodować błędy, gdy nadklasa przyjmuje założenia dotyczące stanu tej zmiennej. Utworzenie chronionej metody ustawiającej dla zmiennej zachowuje tę kontrolę.
Ustaw punkt przerwania lub dodaj dane wyjściowe debugowania, gdy zmienna jest odczytywana lub zapisywana.
Zmień nazwę tej zmiennej składowej bez przeszukiwania całego kodu, który może jej używać.
Ogólnie myślę, że byłby to rzadki przypadek, w którym zalecałbym utworzenie chronionej zmiennej składowej. Lepiej poświęcić kilka minut na ujawnianie właściwości za pomocą metod pobierających / ustawiających, niż godzin później na śledzenie błędu w jakimś innym kodzie, który modyfikował chronioną zmienną. Nie tylko to, ale jesteś ubezpieczony przed dodawaniem przyszłych funkcji (takich jak leniwe ładowanie) bez przerywania zależnego kodu. Trudniej to zrobić później niż teraz.
Na poziomie projektowania może być właściwe użycie chronionej właściwości, ale w przypadku implementacji nie widzę korzyści w mapowaniu jej na chronioną zmienną składową zamiast metod akcesora / mutatora.
Chronione zmienne członkowskie mają znaczące wady, ponieważ skutecznie umożliwiają kodowi klienta (podklasie) dostęp do wewnętrznego stanu klasy bazowej. Zapobiega to efektywnemu utrzymywaniu niezmienników przez klasę bazową.
Z tego samego powodu chronione zmienne składowe również znacznie utrudniają pisanie bezpiecznego kodu wielowątkowego, chyba że gwarantowana stała lub ograniczona do pojedynczego wątku.
Metody Accessor / mutator oferują znacznie większą stabilność API i elastyczność implementacji w trakcie konserwacji.
Ponadto, jeśli jesteś purystą OO, obiekty współpracują / komunikują się, wysyłając wiadomości, a nie czytając / ustawiając stan.
W zamian oferują bardzo niewiele korzyści. Niekoniecznie bym je usunął z cudzego kodu, ale sam ich nie używam.
W większości przypadków korzystanie z funkcji chronionych jest niebezpieczne, ponieważ w pewnym stopniu zrywasz hermetyzację swojej klasy, co może zostać zepsute przez źle zaprojektowaną klasę pochodną.
Ale mam jeden dobry przykład: powiedzmy, że możesz użyć jakiegoś ogólnego pojemnika. Ma wewnętrzną implementację i wewnętrzne akcesory. Ale musisz zaoferować co najmniej 3 publiczny dostęp do jego danych: mapa, hash_map, vector-like. Wtedy masz coś takiego:
template <typename T, typename TContainer>
class Base
{
// etc.
protected
TContainer container ;
}
template <typename Key, typename T>
class DerivedMap : public Base<T, std::map<Key, T> > { /* etc. */ }
template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }
template <typename T>
class DerivedVector : public Base<T, std::vector<T> > { /* etc. */ }
Użyłem tego rodzaju kodu mniej niż miesiąc temu (więc kod pochodzi z pamięci). Po namyśle uważam, że chociaż ogólny kontener Base powinien być klasą abstrakcyjną, to nawet jeśli może całkiem dobrze żyć, bo używanie bezpośrednio Base byłoby tak uciążliwe, że powinno być zabronione.
Podsumowanie W ten sposób masz chronione dane używane przez klasę pochodną. Musimy jednak wziąć pod uwagę fakt, że klasa Base powinna być abstrakcyjna.
protected
nie jest bardziej zamknięty niż public
. Chcę udowodnić, że się mylę. Wszystko, co musisz zrobić, to napisać zajęcia z chronionym członkiem i zabronić mi ich modyfikowania. Oczywiście klasa nie może być ostateczna, ponieważ cały sens używania obiektu protected jest do dziedziczenia. Albo coś jest zamknięte, albo nie. Nie ma stanu pośredniego.
Krótko mówiąc, tak.
Chronione zmienne składowe umożliwiają dostęp do zmiennej z dowolnych podklas, a także z dowolnych klas w tym samym pakiecie. Może to być bardzo przydatne, szczególnie w przypadku danych tylko do odczytu. Nie sądzę, aby były one kiedykolwiek potrzebne, ponieważ każde użycie chronionej zmiennej składowej można replikować przy użyciu prywatnej zmiennej składowej oraz kilku metod pobierających i ustawiających.
Szczegółowe informacje na temat modyfikatorów dostępu do .Net można znaleźć tutaj
Chronione zmienne składowe nie mają żadnych rzeczywistych zalet ani wad, jest to kwestia tego, czego potrzebujesz w konkretnej sytuacji. Ogólnie przyjęta praktyka polega na deklarowaniu zmiennych składowych jako prywatnych i umożliwieniu dostępu z zewnątrz poprzez właściwości. Ponadto niektóre narzędzia (np. Niektóre mapery O / R) oczekują, że dane obiektu będą reprezentowane przez właściwości i nie rozpoznają publicznych lub chronionych zmiennych składowych. Ale jeśli wiesz, że chcesz, aby twoje podklasy (i TYLKO twoje podklasy) miały dostęp do określonej zmiennej, nie ma powodu, aby nie deklarować jej jako chronionej.