Come sappiamo che favorire la composizione rispetto alla generalizzazione è sempre la scelta giusta?


9

Se un oggetto esiste fisicamente o no, possiamo scegliere di modellarlo in diversi modi. In molti casi potremmo usare arbitrariamente la generalizzazione o la composizione. Tuttavia, il principio GoF di "favorire la composizione rispetto alla generalizzazione [sic]" ci guida all'utilizzo della composizione. Quindi, quando modelliamo, ad esempio, una linea, creiamo una classe che contiene due membri PointA e PointB del tipo Point (composizione) anziché estendere Point (generalizzazione). Questo è solo un esempio semplificato di come possiamo arbitrariamente scegliere la composizione o l'eredità da modellare, nonostante gli oggetti siano generalmente molto più complessi.

Come facciamo a sapere che questa è la scelta giusta? Importa almeno perché potrebbe esserci un sacco di refactoring da fare se è sbagliato?


9
Il tuo esempio in realtà non funziona perché non puoi dire che una linea sia un punto, e quindi fallisce il principio di sostituzione di Liskov e l'eredità non è appropriata.
Dean Harding,

@Dean: Come probabilmente saprai, l'esempio non è centrale da indicare. Per la cronaca, una linea può essere rappresentata come un'equazione definita attraverso due punti.
Carney,


1
@Songo: sembra che tu abbia perso del tutto il punto.
Carney,

L'estensione della specializzazione non è la generalizzazione?
Tulains Córdova,

Risposte:


17

Non è sempre la scelta giusta. È quello favorevole nella maggior parte dei casi. Quando un modello composito richiede modifiche o estensioni, è notevolmente più resistente a ciò perché è possibile modificare la composizione senza timore di influire inavvertitamente su altre classi.

Come facciamo a saperlo? Dall'esperienza e dall'esperienza degli altri.

Ho visto molte situazioni in cui una massiccia gerarchia di classi è cresciuta da quello che era iniziato come un semplice concetto, ed è qui che diventa necessario il tuo grande refactoring. Nel frattempo non ho mai visto una situazione in cui una struttura compositiva necessitava di essere trasformata in eredità.

Ma le mie prove aneddotiche non dovrebbero essere sufficienti. Basta guardare in Internet e un buon numero di persone ha avuto le stesse esperienze.


Mi piace; prendere un voto.
Carney,

+1 per "Non ho mai visto una situazione in cui una struttura compositiva necessitava di essere trasformata in eredità".
Kazark,

7

Se vuoi una regola empirica più forte oltre "favorire la composizione sull'eredità", allora potrei suggerire qualcosa del genere:

Dei due modi per specializzare un oggetto - Ereditarietà e Composizione - dovresti usare l'ereditarietà solo quando hai bisogno che il tuo oggetto sia polimorfico (sostituibile) per la classe base che stai specializzando.

Tuttavia, come tutte le regole empiriche, una volta compresa la regola, sei libero di infrangere la regola :)


0

Non vedo come la composizione e la generalizzazione siano alternative e non sono riuscito a trovare la citazione .
Composizione ed eredità sono (per raggiungere la specializzazione) e si può sostenere che l'astrazione e la generalizzazione sono (per raggiungere la modularità).

Quello che vuoi è che il tuo design sia semplice e plausibile, che sono due qualità intrinsecamente abbastanza facilmente misurabili.
Nel tuo caso, sembra più plausibile comporre una linea di due punti invece di estenderla, perché definiresti naturalmente una linea di due punti, piuttosto che estendere il concetto di punto.


È più naturale? Una linea non ha più di due punti di quanto non sia un singolo punto esteso?
Carney,

2
Non ho mai visto una definizione di linea che non includa il fatto che è composta da 2+ punti. Anche usando un'equazione di una linea se hai solo 1 valore x nel tuo dominio di numeri, allora è un punto, non una linea.
Rig

0

Il favore si verifica quando entrambi i candidati si qualificano. Il problema indicato nella domanda non riesce su questo account, perché ha solo un'opzione di composizione.

"Anche la composizione potrebbe usare la generalizzazione". Questa affermazione ha senso per noi? In caso contrario, non siamo ancora pronti a comprendere la regola del "favore".

Il motivo per cui stiamo favorendo Composizione è che Composizione offre più possibilità di estensione / flessibilità rispetto alla generalizzazione. Questa estensione / flessibilità si riferisce principalmente alla flessibilità runtime / dinamica (che si ottiene con la combinazione di interfacce e composizione).

Il vantaggio non è immediatamente visibile. Per vedere il vantaggio è necessario attendere la successiva richiesta di modifica imprevista. Quindi, nella maggior parte dei casi, quelli attaccati alla generalizzazione falliscono rispetto a quelli che hanno abbracciato la composizione (tranne un caso ovvio menzionato più avanti). Da qui la regola. Da un punto di vista dell'apprendimento se riesci a implementare con successo un'iniezione di dipendenza, dovresti sapere quale favorire e quando. La regola ti aiuta a prendere una decisione quando non sei sicuro di quale scegliere. Ancora una volta dovresti essere in grado di vedere entrambe le opzioni in primo luogo per favorirne una.

Riepilogo: Composizione: l'accoppiamento si riduce semplicemente avendo alcune cose più piccole da inserire in qualcosa di più grande, e l'oggetto più grande richiama semplicemente l'oggetto più piccolo. Generazione: dal punto di vista dell'API di terze parti, la definizione di un metodo che può essere sovrascritto è un impegno più forte rispetto alla definizione di un metodo che può essere chiamato (vincita sicura per la generalizzazione). E non dimenticare mai che con la composizione stai anche utilizzando la generalizzazione, da un'interfaccia anziché da una grande classe. Ma l'intero merito va purtroppo alla composizione.


-4

Oggi ho scritto circa 300 LoC. E non ricordo alcun principio che potrei violare e che non abbia violato. Spero che il refactoring salverà la mia anima.

Se l'astrazione è dettata dall'api del partito 3d, di solito è corretto usare l'ereditarietà. Nel disegno domestico, l'entità deve essere astratta o sigillata. L'entità astratta potenzialmente può avere circa 3 eredi. Non mi piacciono i punti di estensione con un metodo virtuale. Tutto sopra riguarda i miei gusti. Non sto parlando dell'astrazione dell'interfaccia, che è una storia completamente diversa.

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.