Czy klasa powinna wiedzieć o swoich podklasach?

24

Czy klasa powinna wiedzieć o swoich podklasach? Czy klasa powinna na przykład zrobić coś specyficznego dla danej podklasy?

Mój instynkt podpowiada mi, że jest to zły projekt, wydaje się być pewnego rodzaju anty-wzorem.

m4design
źródło
6
Zwykle nie, ale w szczególnych przypadkach jest to przydatne.
CodesInChaos
Jest to anty-wzorzec, dobrze znany pod nazwą: „Zasada podstawienia Liskowa”, a czasem po prostu LSP, przeczytaj o tym na en.wikipedia.org/wiki/Liskov_substitution_principle
Jimmy Hoffa
5
@JimmyHoffa - „Zasada podstawienia Liskowa” nie jest nazwą anty-wzorca. Anty-wzorzec może naruszać LSP, ale nie jest nawet pewne, czy byłoby to prawdą tutaj. Możliwe jest, że nawet jeśli typ wie, że są to podtypy, to podtypy te można nadal zastąpić ich rodzicem kontrawariancją i kowariancją.
Matthew Flynn
4
@MatthewFlynn Być może używam LSP trochę luźno, ale w mojej definicji nie chodzi o to, że wszyscy oni spełniają wymagania każdej innej osoby, która jest zgodna z LSP, jest to wymóg, aby mieli takie oddzielenie, że nie tylko się ze sobą zgadzają, ale możesz zaimplementować inną podklasę, która nie narusza LSP. Jeśli hierarchia jest samoświadoma, wówczas zewnętrzna implementacja nie mogłaby spełnić LSP bez zmiany klasy podstawowej. Być może nie jest to ściśle LSP, ale jest blisko. i tak, powinienem był powiedzieć, że naruszenie LSP jest anty-wzorcem
Jimmy Hoffa,
2
Kilka razy, z którymi się zetknąłem, przekształciłem tę wiedzę w metodę, która mogłaby zostać nadpisana w podklasie (albo wymuszona przez uczynienie jej abstrakcyjną lub czystą wirtualną, albo zapewnienie rozsądnej domyślnej implementacji, którą można zastąpić).

Odpowiedzi:

32

Odpowiedź sugerowana przez pojęcie klas brzmi „nie”.

Niezależnie od tego, jaką akcję, dane lub relację wykonujesz, należy ona do wszystkich podklas - wtedy powinna być obsługiwana w nadklasie bez sprawdzania rzeczywistego typu. Lub odnosi się tylko do niektórych podklas - wtedy trzeba by wykonać sprawdzanie typu w czasie wykonywania, aby zrobić właściwą rzecz, nadklasa musiałaby zostać zmieniona za każdym razem, gdy ktoś inny odziedziczy po niej (lub może po cichu zrobić coś niewłaściwego), zmiany w klasach pochodnych mogą złamać niezmienioną nadklasę itp.

Krótko mówiąc, masz szereg złych konsekwencji, które zwykle są wystarczająco złe, aby odrzucić takie rozwiązania z ręki. Jeśli kilka podklas robi to samo i chcesz uniknąć powielania kodu (praktycznie zawsze jest to dobra rzecz), lepszym rozwiązaniem jest wprowadzenie klasy średniej, z której wszystkie te podklasy mogą odziedziczyć kod.

Kilian Foth
źródło
4
Jeden wyjątek od tej reguły: Dokumentacja klasy powinna zawierać listę jej podklas lokalnych dla własnej biblioteki.
Kieveli
3
@Kieveli ... dopóki ta dokumentacja jest aktualna.
mouviciel
14
Każde użyteczne narzędzie do dokumentacji powinno być w stanie narysować drzewa spadkowe ...
John
13
Nie sądzę, że to wyjątek. Klasa wciąż nic nie wie. Dokumentacja wie.
Honza Brabec
4
@Kieveli uhh Całkowicie się nie zgadzam.
Jimmy Hoffa
16

Nie tylko nie powinien wiedzieć, ale po prostu nie może ! Zazwyczaj zajęcia mogą być przedłużane w dowolnym miejscu i czasie. Można go rozszerzyć o klasy, które nawet nie istniały, kiedy zostały napisane.

Niektóre języki pozwalają na kontrolowanie rozszerzania klas przez nadklasę. W Scali klasę można oznaczyć jako sealed, co oznacza, że ​​można ją rozszerzyć tylko o inne klasy w tej samej jednostce kompilacji (plik źródłowy). Jednakże, chyba że te podklasy są również sealed lub final, podklasy można następnie rozszerzyć o inne klasy.

W Scali służy do modelowania zamkniętych typów danych algebraicznych, więc kanoniczny Listtyp Haskell :

data List a = Nil | Cons a (List a)

można modelować w Scali w następujący sposób:

sealed trait List[+A]

case object Nil extends List[Nothing]

final case class Cons[+A] extends List[A]

I można zagwarantować, że tylko opuszczeniu w tych dwóch podsystem „klas”, bo Listjest sealedi nie może być przedłużony poza pliku, Consjest finali nie może być rozszerzony na wszystkich i Nilto object, które nie mogą zostać rozszerzone tak.

Ale jest to szczególny przypadek użycia (modelowanie algebraicznych typów danych poprzez dziedziczenie) i nawet w tym przypadku nadklasa tak naprawdę nie wie o swoich podklasach. Jest to bardziej gwarancja dla użytkownika tego Listtypu, że jeśli podejmie dyskryminację Nili Consnie będzie żadnej innej alternatywy wyskakującej za jego plecami.

Jörg W Mittag
źródło
W wielu językach działa dodawanie elementów abstract internalczłonkowskich.
CodesInChaos
4

Prostą odpowiedzią jest: nie.

Sprawia, że ​​kod jest kruchy i unieważnia dwie podstawowe zasady programowania obiektowego.

  • Liskov Substytucja
  • Zasada otwartego zamknięcia
Sajad Deyargaroo
źródło
1

Tak czasami. Na przykład, gdy istnieje ograniczona liczba podklas. Odwiedzający jest ilustracją użyteczność tego podejścia.

Przykład: abstrakcyjne węzły drzewa syntaktycznego (AST) o dobrze zdefiniowanej gramatyce mogą być dziedziczone z pojedynczej Nodeklasy implementującej wzorzec gościa do obsługi wszystkich typów węzłów.

lorus
źródło
9
Nie, nie i nie. To nie jest przydatne i nigdy nie jest dobrym rozwiązaniem.
Sulthan
@Sulthan Pracowałem z AST poza klasą i wierzę, że lorus nie rozumie tego pytania. Gość nie jest rodzajem węzła w sieci AST, jest to osobny obiekt. Może i powinien wiedzieć o obiektach gramatycznych. Ale węzeł wysokiego poziomu AST nie powinien.
1
@JohnGaughan Pojęcie „wie” wprowadza w błąd. NodeKlasa podstawowa oczywiście nie zawiera żadnych bezpośrednich odniesień do jej podklas. Ale zawiera acceptmetodę z Visitorparametrem. I Visitorzawiera visitmetodę dla każdej podklasy. Mimo że Nodenie ma bezpośrednich odniesień do swoich podklas, „wie” o nich pośrednio, poprzez Visitorinterfejs. Wszyscy przez to połączyli się.
lorus
1
@Sulthan Nigdy nie mów nigdy. Typ opcji jest poprawnym przykładem przypadku, gdy nadklasa wie o potomku. Ale jak powiedział Jorg, będzie oznaczony jako zapieczętowany.
Pavel Voronin,
W takim razie zgadzają się: odwiedzający musi wiedzieć, co odwiedza.
1

Jeśli piszę komponent dla firmy, a później, po odejściu, ktoś rozszerza go na własne potrzeby, czy powinienem zostać o tym poinformowany?

Nie!

To samo z klasami. Zaufaj instynktowi.

Daniel Hollinrake
źródło
Bezpieczeństwo pracy???
sixtyfootersdude
Masz sześćdziesiąt stóp wzrostu, więc nie będę się z tobą kłócił :-) Mam jednak nadzieję, że moje znaczenie miało miejsce po tym, jak odszedłem i dostałem inną pracę.
Daniel Hollinrake