Una classe dovrebbe sapere delle sue sottoclassi?


24

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.


6
Di solito no, ma in casi speciali è utile.
CodesInChaos,

Si tratta di un anti-modello, un noto uno chiamato: "Liskov principio di sostituzione" o talvolta semplicemente LSP, letto su di esso a en.wikipedia.org/wiki/Liskov_substitution_principle
Jimmy Hoffa

5
@JimmyHoffa - "Principio di sostituzione di Liskov" non è il nome di un anti-schema. Un anti-pattern può violare LSP, ma non è nemmeno sicuro che questo sia vero qui. È possibile che, anche se un tipo sappia che sono sottotipi, i sottotipi possano ancora essere sostituiti al genitore con contravarianza e covarianza.
Matthew Flynn,

4
@MatthewFlynn Forse sto usando LSP un po 'vagamente ma nella mia definizione non è il fatto che soddisfino tutti i requisiti di ciascuno che sia conforme a LSP, è un requisito che hanno un disaccoppiamento tale che non solo sono d'accordo tra loro ma potresti implementare un'altra sottoclasse che non viola LSP. Se la gerarchia è autocosciente, un'implementazione esterna non potrebbe assolutamente soddisfare LSP senza alterare la classe base. Forse non è strettamente LSP ma è molto vicino. e sì, avrei dovuto dire che la violazione di LSP è l'anti-modello
Jimmy Hoffa,

2
Le poche volte che l'ho incontrato, ho riformulato quella conoscenza in un metodo che potrebbe essere ignorato in una sottoclasse (o forzato rendendolo astratto o puro virtuale, o fornendo un'implementazione di default sensata che potrebbe essere sovrascritta).

Risposte:


32

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.


4
Un'eccezione a questa regola: la documentazione della classe dovrebbe elencare le sue sottoclassi locali nella propria libreria.
Kieveli,

3
@Kieveli ... purché la documentazione sia aggiornata.
mouviciel,

14
Qualsiasi strumento di documentazione utilizzabile dovrebbe essere in grado di disegnare alberi ereditari ...
johannes,

13
Non penso che sia un'eccezione. La classe continua a non sapere nulla. La documentazione lo sa.
Honza Brabec,

4
@Kieveli uhh non sono completamente d' accordo.
Jimmy Hoffa,

16

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 Listtipo 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è sealede quindi non può essere esteso al di fuori del file, Consè finale quindi non può essere esteso affatto ed Nilè objectcomunque 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 Listtipo che se fa una discriminazione del caso Nile Cons, non ci sarà alcuna altra alternativa spuntargli alle spalle.


Ciò che funziona in molte lingue è l'aggiunta di una qualche forma di abstract internalmembro.
CodesInChaos,

4

La semplice risposta è No.

Rende il codice fragile e svuota due principi base della programmazione orientata agli oggetti.

  • Principio di sostituzione di Liskov
  • Principio Open Close

1

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 Nodeclasse che implementa un modello visitatore per gestire tutti i tipi di nodo.


9
No, no e no. Questa non è utile e mai una buona soluzione.
Sulthan,

@Sulthan Ho lavorato con AST al di fuori della classe e credo che Loro non capisca veramente la domanda. Un visitatore non è un tipo di nodo su un AST, è un oggetto separato. Può e dovrebbe conoscere gli oggetti grammaticali. Ma un nodo AST di alto livello non dovrebbe.

1
@JohnGaughan Il termine "sa" mi confonde. La Nodeclasse base , ovviamente, non contiene riferimenti diretti alle sue sottoclassi. Ma contiene un acceptmetodo con un Visitorparametro. E Visitorcontiene un visitmetodo per ogni sottoclasse. Quindi, nonostante Nodenon abbia riferimenti diretti alle sue sottoclassi, "le conosce" indirettamente attraverso l' Visitorinterfaccia. Si accoppiarono tutti insieme.
loro,

1
@Sulthan Non dire mai mai. Il tipo di opzione è un esempio valido del caso in cui la super-classe sia a conoscenza di quella discendente. Ma come ha detto Jorg sarebbe stato contrassegnato come sigillato.
Pavel Voronin,

Quindi sono d'accordo: un visitatore deve sapere cosa sta visitando.

1

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.


Sicurezza sul lavoro???
sixtyfootersdude,

Il tuo sessanta piedi di altezza, quindi non discuterò con te :-) Tuttavia, spero che il mio significato fosse dopo che sono andato e ho trovato un altro lavoro.
Daniel Hollinrake,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.