Una classe dovrebbe sapere delle sue sottoclassi? Una classe dovrebbe fare qualcosa di specifico per una determinata sottoclasse, ad esempio?
Il mio istinto mi dice che è un cattivo design, sembra un anti-pattern di qualche tipo.
Una classe dovrebbe sapere delle sue sottoclassi? Una classe dovrebbe fare qualcosa di specifico per una determinata sottoclasse, ad esempio?
Il mio istinto mi dice che è un cattivo design, sembra un anti-pattern di qualche tipo.
Risposte:
La risposta implicita nel concetto di classi è "no".
Qualunque azione, dato o relazione tu stia gestendo fa parte di tutte le sottoclassi, quindi dovrebbe essere gestita nella superclasse senza controllare il tipo effettivo. Oppure si applica solo ad alcune sottoclassi - quindi dovresti eseguire controlli del tipo di runtime per fare la cosa giusta, la superclasse dovrebbe essere cambiata ogni volta che qualcun altro eredita da essa (o potrebbe fare silenziosamente la cosa sbagliata), i cambiamenti nelle classi derivate possono interrompere la superclasse invariata, ecc.
In breve, si ottengono una serie di conseguenze negative che di solito sono abbastanza gravi da rifiutare tali soluzioni. Se più sottoclassi fanno la stessa cosa e vuoi evitare la duplicazione del codice (praticamente sempre una buona cosa), una soluzione migliore è quella di introdurre una classe di medio livello da cui tutte quelle sottoclassi possano ereditare il codice.
Non solo non dovrebbe saperlo, semplicemente non può ! Di solito, una classe può essere estesa sempre e ovunque. Può essere esteso da classi che non esistevano nemmeno quando è stato scritto.
Alcune lingue consentono alle classi estese di essere controllate dalla superclasse. In Scala, una classe può essere contrassegnata come sealed
, il che significa che può essere estesa solo da altre classi all'interno della stessa unità di compilazione (file sorgente). Tuttavia, a meno che tali sottoclassi non siano anche sealed
o final
, le sottoclassi possono essere ulteriormente estese da altre classi.
In Scala, questo è usato per modellare tipi di dati algebrici chiusi, quindi il List
tipo canonico di Haskell :
data List a = Nil | Cons a (List a)
può essere modellato in Scala in questo modo:
sealed trait List[+A]
case object Nil extends List[Nothing]
final case class Cons[+A] extends List[A]
E puoi garantire che esistono solo queste due "sottoclassi" perché List
è sealed
e quindi non può essere esteso al di fuori del file, Cons
è final
e quindi non può essere esteso affatto ed Nil
è object
comunque impossibile estenderlo.
Ma questo è un caso d'uso specifico (modellizzazione di tipi di dati algebrici tramite ereditarietà) e anche in questo caso, la superclasse in realtà non è a conoscenza delle sue sottoclassi. È più una garanzia per l' utente del List
tipo che se fa una discriminazione del caso Nil
e Cons
, non ci sarà alcuna altra alternativa spuntargli alle spalle.
abstract internal
membro.
La semplice risposta è No.
Rende il codice fragile e svuota due principi base della programmazione orientata agli oggetti.
Si Qualche volta. Ad esempio, quando esiste un numero limitato di sottoclassi. Un modello di visitatore è un'illustrazione dell'utilità di questo approccio.
Esempio: un nodo AST (abstract syntax tree) di una grammatica ben definita può essere ereditato da una singola Node
classe che implementa un modello visitatore per gestire tutti i tipi di nodo.
Node
classe base , ovviamente, non contiene riferimenti diretti alle sue sottoclassi. Ma contiene un accept
metodo con un Visitor
parametro. E Visitor
contiene un visit
metodo per ogni sottoclasse. Quindi, nonostante Node
non abbia riferimenti diretti alle sue sottoclassi, "le conosce" indirettamente attraverso l' Visitor
interfaccia. Si accoppiarono tutti insieme.
Se scrivo un componente per un'azienda e successivamente, dopo che me ne sono andato, qualcuno lo estende per i propri scopi, dovrei esserne informato?
No!
Lo stesso vale per le lezioni. Fidati del tuo istinto.