Quando utilizzare l'ereditarietà, quando utilizzare "solo un campo booleano"?


18

Nella nostra applicazione Rails, stiamo aggiungendo notifiche. Alcuni di questi sono blocking: arrestano l'avanzamento di qualunque risorsa vengano aggiunti, poiché mancano alcune informazioni su quella risorsa.

Altre notifiche sono semplici notifiche e forniscono solo informazioni.

Oggi ho avuto una discussione con un altro programmatore nel nostro team. Ho creato la struttura ereditaria in questo modo:

inserisci qui la descrizione dell'immagine

Tuttavia, preferirebbe che io aggiungessi semplicemente blockingcome metodo di ritorno booleano su ciascuna notifica e specificasse un elenco di sottoclassi che si bloccano all'interno della classe genitore di notifica.

La differenza tra questi approcci non è molto grande; nel mio approccio non è necessario specificare questo elenco, mantenendo la classe radice più pulita. D'altra parte, Notification::Blockingneanche la logica speciale che accade in questo momento non è molto ampia.

Quale tipo di astrazione è più adatta a questo problema?


11
Una classe genitore non dovrebbe mai sapere dei suoi figli. Perché è necessario mantenere un elenco di sottoclassi?
Coteyr,

Come vengono aggiunti a una risorsa e come si fermano i suoi progressi?
null

3
Perché hai bisogno di così tante classi di notifica? Mi sembra che tu possa creare una classe di notifica, quindi lasciare che i dati guidino le azioni anziché i tipi di dati.
Trisped

1
@Trisped: giusto, se ti ritrovi a spostare un aspetto del comportamento nella classe base con un elenco esaustivo di casi, allora abbi il coraggio delle tue convinzioni, ammetti che non stai davvero progettando una classe base utilizzabile per la personalizzazione per estensione e sposta tutto il comportamento nella classe base!
Steve Jessop,

Risposte:


35

Si desidera evitare che le classi base conoscano le classi derivate. Presenta un accoppiamento stretto ed è un mal di testa di manutenzione perché devi ricordare di aggiungere all'elenco ogni volta che crei una nuova classe derivata.

Ti impedirà inoltre di poter inserire la classe di notifica in un pacchetto / assembly riutilizzabile se desideri utilizzare questa classe in più progetti.

Se vuoi davvero usare una singola classe base, un altro modo per risolverlo è aggiungere una proprietà virtuale o un metodo IsBlocking sulla classe di notifica base. Le classi derivate potrebbero quindi sovrascriverle per restituire vero o falso. Avresti una soluzione a classe singola senza che la classe base fosse a conoscenza delle classi derivate.


3
Questo. Prendi decisioni in un unico posto. Non disperdere la conoscenza di quali classi si bloccano tra la classe e l'elenco.
candied_orange,

Questo è quello che faccio, funziona molto bene ed è molto riutilizzabile.
Coteyr,

13

e specifica un elenco di sottoclassi che stanno bloccando all'interno della classe genitore di notifica.

Sembra molto particolare ed è un odore particolare di codice.

Fornirei sottoclassi se si riscontrano differenze nel comportamento tra le classi e si desidera trattare tutte queste notifiche allo stesso modo (ovvero utilizzando il polimorfismo ).


1
Penso che il "comportamento" sia la chiave qui: quando si tratta solo di dati, il campo dovrebbe essere un discriminatore sufficiente. Il comportamento è la ragione migliore per usare il polimorfismo, tuttavia si dovrebbe sempre considerare la complessità della manutenzione quando si creano gerarchie ereditarie. Leggi en.wikipedia.org/wiki/Composition_over_inheritance
cottsak

7

Per invertire le risposte esistenti, suggerirei che la proprietà booleana sia l'opzione migliore se la modalità da utilizzare deve essere modificata in modo dinamico (ad esempio tramite un file di configurazione che fornisce un elenco di quali tipi devono essere bloccati e quali non lo sono).

Detto questo, un design migliore anche in questa situazione potrebbe essere l'uso di un oggetto Decorator.


1

Direi che dipende da quanto altro è speciale per una notifica di blocco, anche se il mio primo pensiero è di andare con "entrambi":

class Notification
 virtual Boolean Blocking{get return false;}

class BlockingNotification inherits Notification
 virtual overrides Boolean Blocking{get return true;}

In questo modo, puoi usare n.Blockingo n is BlockingNotification(tutto in pseudo-codice), sebbene, se hai intenzione di consentire a una classe di implementare un Blockingvalore sensibile al contesto , visto che dovresti controllare quel valore ogni volta, la BlockingNotificationclasse diventa meno utile.

In ogni caso, sono d'accordo con le altre risposte che non desideri che l'implementazione della classe base Blockingdebba conoscere le classi derivate.


0

Invece di creare due classi di base e più istanze di ciascuna, crea una classe di notifica con un bool per indicare se la notifica è bloccata e qualsiasi altra informazione necessaria per comunicare la notifica all'utente.

Ciò consente di utilizzare un set di codice per l'elaborazione e la presentazione delle notifiche e riduce la complessità del codice.

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.