Perché la sottoclasse è troppo brutta (e quindi perché dovremmo usare i prototipi per farla franca)?

9

Stavo leggendo gli schemi di progettazione e ho letto che il modello di progettazione del prototipo elimina le sottoclassi eccessive.

Perché la sottoclasse è cattiva? Quale vantaggio avrebbe l'utilizzo di un prototipo sulla sottoclasse?

    
posta David Faux 01.03.2012 - 03:13
fonte

6 risposte

18

Perché la sottoclasse è troppo brutta

"Troppo" è una chiamata di giudizio; ma prendilo da me è cattivo. Il codice con cui lavoro ha ereditarietà DEEP e il codice è dannatamente difficile da leggere, capire, seguire, tracciare, eseguire il debug, ecc. Ecc. È impossibile scrivere codice di prova per questa roba.

Lo schema del prototipo elimina la sottoclasse

Oppure la domanda significa "elimina troppe sottoclassi?". Questo schema richiede la clonazione di un oggetto "modello" per evitare la sottoclasse, almeno a quel punto. Non esiste una regola che dica che il "modello" non può essere una sottoclasse.

Favorisci la composizione sull'ereditarietà

Questa idea qui include anche la delega e l'aggregazione. Seguendo questo mezzo euristico il tuo software tende ad essere più flessibile, più facile da mantenere, estendere e riutilizzare.

Quando una classe è composta da parti puoi sostituire quelle parti in fase di runtime . Ciò ha un profondo effetto sulla testabilità.

Il test è più semplice . È possibile utilizzare parti contraffatte (ad es. "Mock", "doubles" e altri test-talk). L'ereditarietà del nostro codice significa che dobbiamo istanziare l'intera gerarchia per testarne qualcuna. Nel nostro caso ciò non è possibile senza eseguire il codice nel suo ambiente reale. Ad esempio abbiamo bisogno di un database per creare istanze di oggetti di business.

I cambiamenti arrivano con effetti collaterali e incertezza - Più "base" la classe, più gli effetti sono diffusi, nel bene e nel male. Ci possono essere cambiamenti desiderati che non osi fare a causa dell'incertezza degli effetti collaterali. O un cambiamento che va bene per qualche posto nella nostra catena ereditaria è negativo per un altro. Questa è sicuramente la mia esperienza.

    
risposta data 01.03.2012 - 04:45
fonte
8

Penso che uno dei motivi per cui questo è stato visto come una cattiva pratica è che nel tempo ogni sottoclasse può contenere attributi e metodi che non hanno senso per quell'oggetto più si scende lungo la catena (in una gerarchia poco sviluppata). Puoi finire con una classe gonfia che contiene elementi che non hanno senso per quella classe.

Sono agnostico su questo me stesso. Sembra dogmatico dire solo che è male. Qualcosa non va bene se usato male.

    
risposta data 01.03.2012 - 04:00
fonte
2

Due motivi.

  1. è la prestazione di runtime. Ogni volta che invochi una sottoclasse da una superclasse stai facendo una chiamata al metodo, quindi se hai nidificato cinque classi e invochi un metodo nella classe base farai cinque invocazioni di metodi. La maggior parte dei compilatori / runtime cercheranno di riconoscerlo e ottimizzeranno le chiamate extra, ma è sicuro farlo solo per casi molto semplici.

  2. È la sanità mentale dei tuoi colleghi programmatori. Se nidifichi cinque classi, devo esaminare tutte e quattro le classi genitore per stabilire che stai davvero chiamando un metodo nella classe base, diventa noioso. Potrei anche dover passare attraverso cinque invocazioni del metodo quando eseguo il debug del programma.

risposta data 01.03.2012 - 04:05
fonte
2

"Troppo" è un giudizio.

Come per la maggior parte delle cose nella programmazione, la sottoclassificazione non è intrinsecamente quando ha senso. Quando il modello della soluzione del problema ha più senso come gerarchia piuttosto che come una composizione di parti, la sottoclasse potrebbe essere l'opzione preferita.

Detto questo, la preferenza per la composizione sull'ereditarietà è una buona regola empirica.

Quando si sottoclasse, il test può essere più difficile (come radarbob notato ). In una gerarchia profonda, isolare il codice problematico è più difficile perché l'istanziazione di una sottoclasse richiede l'introduzione dell'intera gerarchia della superclasse e di un gruppo di codice aggiuntivo. Con un approccio compositivo, puoi isolare e testare ogni componente del tuo oggetto aggregato con test unitari, oggetti finti, ecc.

La sottoclasse può anche farti esporre i dettagli della tua implementazione che potresti non volere. Ciò rende difficile controllare l'accesso alla rappresentazione sottostante dei dati e che ti lega a una particolare implementazione del tuo modello di backup.

Un esempio che posso pensare è java.util.Properties, che eredita da Hashtable. La classe Properties è intesa solo per memorizzare coppie String-String (questo è annotato in Javadocs). A causa dell'ereditarietà, i metodi put e putAll di Hashtable sono stati esposti agli utenti di Properties. Mentre il modo "giusto" per memorizzare una proprietà è con setProperty (), non c'è assolutamente nulla che impedisca a un utente di chiamare put () e passare una stringa e un oggetto.

Fondamentalmente, la semantica delle Proprietà è mal definita per via dell'ereditarietà. Inoltre, qualora qualcuno volesse modificare l'oggetto di supporto per Proprietà, l'impatto avrà un impatto molto maggiore sul codice che utilizza Proprietà rispetto a se le Proprietà avessero adottato un approccio più compositivo.

    
risposta data 01.03.2012 - 17:04
fonte
2

L'ereditarietà può spezzare l'incapsulamento in alcuni casi e, se ciò accade, è negativo. Leggi di più.

In bei vecchi tempi senza ereditarietà, una libreria pubblicava un'API e l'applicazione che lo utilizza utilizza ciò che è pubblicamente visibile e la biblioteca ora ha il proprio modo di gestire gli ingranaggi interni che continuano a cambiare senza rompere l'app.

Man mano che le esigenze evolvono, la libreria può evolversi e può avere più clienti; oppure può fornire funzionalità estese ereditando dalla propria classe con altri sapori. Fin qui tutto bene.

Tuttavia, la cosa fondamentale è che se un'applicazione prende la classe e inizia a suddividerla, ora una sottoclasse è in realtà un client piuttosto che un partner interno. Tuttavia, a differenza di un chiaro modo inequivocabile con cui viene definita l'API pubblica, la sottoclasse è ora molto più profonda all'interno della libreria (accesso a tutte le variabili private e così via). Non è ancora male, ma un personaggio cattivo può fare il bridge di un'API che è troppo all'interno dell'APP e nella libreria-

In sostanza, anche se questo potrebbe non essere sempre il caso,

It is easy to misuse inheritance to break encapsulation

Consentire la sottoclasse può essere sbagliato se questo punto.

    
risposta data 04.03.2012 - 07:55
fonte
1

Breve risposta rapida:

Dipende da cosa stai facendo.

Risposta noiosa estesa:

Uno sviluppatore può creare un'app. utilizzando entrambi i modelli, sottoclasse o (prototipo). A volte una tecnica funziona meglio. A volte l'altra tecnica funziona meglio.

Scegliere con la tecnica funziona meglio, richiede anche, considerare se è presente uno scenario di "Eredità multipla". Anche se il linguaggio di programmazione supporta solo ereditarietà, modelli o prototipi.

Nota complementare:

Alcune risposte si riferiscono alla "eredità multipla". Altought, non la domanda principale, è legata all'argomento. Ci sono diversi post simili su "multiple inheritance v.s. composition". Ho avuto un caso in cui mescolo entrambi.

    
risposta data 04.03.2012 - 18:11
fonte

Leggi altre domande sui tag