Gerarchie parallele - in parte uguali, in parte diverse


12

Ci sono alcune domande simili là fuori 1 ,2 ,3 ,4 , ma non sembra esattamente il caso in questa domanda, né le soluzioni sembrano ottimali.

Questa è una domanda OOP generale, supponendo che siano disponibili polimorfismo, generici e mixin. Il linguaggio effettivo da utilizzare è OOP Javascript (Typescript), ma è lo stesso problema in Java o C ++.

Ho gerarchie di classi parallele, che a volte condividono lo stesso comportamento (interfaccia e implementazione), ma a volte ognuna ha il suo comportamento "protetto". Illustrato in questo modo:

3 gerarchie di classi parallele, la colonna centrale mostra le parti comuni, la colonna sinistra è la gerarchia del canvas e la colonna destra mostra la gerarchia SVG

Questo è solo a scopo illustrativo ; non è il diagramma di classe attuale. Per leggerlo:

  • Qualsiasi cosa nella gerarchia comune (al centro) è condivisa tra le gerarchie Canvas (a sinistra) e SVG (a destra). Per condivisione intendo sia l'interfaccia che l'implementazione.
  • Tutto ciò che riguarda solo le colonne sinistra o destra indica un comportamento (metodi e membri) specifico per quella gerarchia. Per esempio:
    • Sia la gerarchia sinistra che quella destra usano esattamente gli stessi meccanismi di validazione, mostrati come un singolo metodo ( Viewee.validate()) nella gerarchia comune.
    • Solo la gerarchia della tela ha un metodo paint(). Questo metodo chiama il metodo di disegno su tutti i bambini.
    • La gerarchia SVG deve sovrascrivere il addChild()metodo di Composite, ma non è così per la gerarchia del canvas.
  • I costrutti delle due gerarchie laterali non possono essere mescolati. Una fabbrica lo assicura.

Soluzione I: stuzzicare l'ereditarietà

Fowler's Tease Apart Ereditarietà non sembra fare il lavoro qui, perché c'è qualche discrepanza tra i due paralleli.

Soluzione II - Mixin

Questo è l'unico a cui riesco a pensare. Le due gerarchie sono sviluppate separatamente, ma ad ogni livello le classi si mescolano alla classe comune, che non fanno parte di una gerarchia di classi. Omettendo la structuralforcella, sarà simile al seguente:

Le tre colonne di nuovo, le colonne sinistra e destra sono gerarchie parallele, in cui ogni classe è inerente anche a una classe comune.  Le classi comuni non fanno parte di una gerarchia

Nota che ogni colonna sarà nel suo spazio dei nomi, quindi i nomi delle classi non entreranno in conflitto.

La domanda

Qualcuno può vedere i difetti con questo approccio? Qualcuno può pensare a una soluzione migliore?


appendice

Ecco alcuni esempi di codice su come utilizzare questo. Lo spazio dei nomi svgpuò essere sostituito con canvas:

var iView        = document.getElementById( 'view' ),
    iKandinsky   = new svg.Kandinsky(),
    iEpigone     = new svg.Epigone(),
    iTonyBlair   = new svg.TonyBlair( iView, iKandinsky ),
    iLayer       = new svg.Layer(),
    iZoomer      = new svg.Zoomer(),
    iFace        = new svg.Rectangle( new Rect( 20, 20, 100, 60) ),
    iEyeL        = new svg.Rectangle( new Rect( 20, 20, 20, 20) ),
    iEyeR        = new svg.Rectangle( new Rect( 60, 20, 20, 20) );

iKandinsky.setContext( iTonyBlair.canvas.getContext( '2d' ) );
iEpigone.setContext( iTonyBlair.canvas.getContext( '2d' ) );

iFace.addChildren( iEyeL, iEyeR );
iZoomer.setZoom( new Point( 2, 2 ) );
iZoomer.addChild( iFace );
iLayer.addChild( iZoomer );
iTonyBlair.setContent( iLayer );

In sostanza, nei client runtime comporre la gerarchia di istanze delle sottoclassi di Viewee; così:

Un'immagine che mostra una gerarchia di oggetti come layer, rect, scroller, ecc.

Supponiamo che tutti questi spettatori provengano dalla gerarchia della tela, che siano resi attraversando la gerarchia che può chiamare paint()ogni spettatore. Se provengono dalla gerarchia svg, i telespettatori sanno come aggiungersi al DOM, ma non c'è paint()attraversamento.



Forse provare il modello di design Decorator completo (Erich Gamma et als, Design Patterns)?
Zon,

Cos'è uno spettatore? Che cosa significa "parallelo" come un sostantivo (al contrario di un aggettivo)?
Tulains Córdova,

Hai eredità multipla?
Tulains Córdova,

Le classi Canvas o SVG contengono stato o dati aggiuntivi che non sono in comune? Come usi le lezioni? Puoi mostrare qualche codice di esempio che mostra come si possono usare questi hiearchie?
Euforico,

Risposte:


5

Il secondo approccio segrega meglio le interfacce, seguendo il principio di segregazione delle interfacce.

Tuttavia, aggiungerei un'interfaccia Paintable.

Vorrei anche cambiare alcuni nomi. Non c'è bisogno di creare confusione:

// common

public interface IComposite {
    public void addChild(Composite e);
}

public interface IViewee extends IComposite{
    public void validate();
    public List<IBound> getAbsoluteBouns();
}

public interface IVisual {
    public List<IBound> getBounds();
}

public interface IRec {
}

public interface IPaintable {
    public void paint();
}

// canvas

public interface ICanvasViewee extends IViewee, IPaintable {
}

public interface ICanvasVisual extends IViewee, IVisual {
}

public interface ICanvasRect extends ICanvasVisual, IRec {
}


// SVG

public interface ISVGViewee extends IViewee {
    public void element();
}

public interface ISVGVisual extends IVisual, ISVGViewee {
}

public interface ISVGRect extends ISVGVisual, IRect {
}

Penso che le interfacce possano aiutare in questo caso. Vorrei sapere i motivi per cui la tua risposta è stata sottoposta a downgrade.
Umlcat,

non il downvoter, ma le interfacce esponenziali di IMHO non sono un buon modello
dagnelies

@arnaud cosa intendi con "interfacce esponenziali"?
Tulains Córdova,

@ user61852 ... beh, diciamo solo che sono molte interfacce. "esponenziale" era in realtà un termine sbagliato, è più simile a "moltiplicativo". Nel senso che se avessi più "sfaccettature" (composito, visivo, verniciabile ...) e più "elementi" (tela, svg ...), finiresti con molte interfacce.
Dagnelies,

@arnaud Hai ragione, ma almeno c'è un design flessibile in anticipo e l'incubo dell'eredità di OP sarebbe risolto quando non ti sentirai costretto a estenderti. Se vuoi, estendi un po 'di classe e non sei forzato da una gerarchia forzata.
Tulains Córdova,

3

Questa è una domanda OOP generale, supponendo che siano disponibili polimorfismo, generici e mixin. Il linguaggio effettivo da utilizzare è OOP Javascript (Typescript), ma è lo stesso problema in Java o C ++.

In realtà non è affatto vero. Typescript ha un vantaggio sostanziale rispetto a Java, vale a dire la tipizzazione strutturale. Puoi fare qualcosa di simile in C ++ con i modelli duck, ma è uno sforzo molto maggiore.

Fondamentalmente, definisci le tue classi, ma non preoccuparti di estendere o definire alcuna interfaccia. Quindi definisci semplicemente l'interfaccia che ti serve e prendila come parametro. Quindi, gli oggetti possono abbinare quell'interfaccia, non hanno bisogno di sapere per estenderlo in anticipo. Ogni funzione può dichiarare esattamente e solo i bit di cui si frega, e il compilatore ti darà un passaggio se il tipo finale lo incontra, anche se le classi in realtà non estendono esplicitamente quelle interfacce.

Questo ti libera dalla necessità di definire effettivamente una gerarchia di interfaccia e definire quali classi dovrebbero estendere quali interfacce.

Basta definire ogni classe e dimenticare le interfacce: la tipizzazione strutturale se ne occuperà.

Per esempio:

class SVGViewee {
    validate() { /* stuff */ }
    addChild(svg: SVG) { /* stuff */ }
}
class CanvasViewee {
    validate() { /* stuff */ }
    paint() { /* stuff */ }
}
interface SVG {
    addChild: { (svg: SVG): void };
}
f(viewee: { validate: { (): boolean }; }) {
    viewee.validate();
}
g(svg: SVG) {
    svg.addChild(svg);
}
h(canvas: { paint: { (): void }; }) {
    canvas.paint();
}
f(SVGViewee());
f(CanvasViewee());
g(SVGViewee());
h(CanvasViewee());

Questo è un dattiloscritto totalmente legittimo. Si noti che le funzioni che consumano non conoscono o danno una merda singola sulle classi di base o sulle interfacce utilizzate nella definizione delle classi.

Non importa se le classi sono correlate o meno per eredità. Non importa se hanno esteso la tua interfaccia. Basta definire l'interfaccia come parametro e il gioco è fatto: tutte le classi che la soddisfano sono accettate.


Sembra promettente, ma non capisco davvero la proposta (scusate, forse troppa inclinazione OOP). Forse puoi condividere qualche esempio di codice? Ad esempio, sia svg.Viewee che canvas.Viewee necessitano di un validate()metodo (la cui implementazione è identica per entrambi); quindi solo svg.Viewee deve sovrascrivere addChild () , mentre solo canvas.Viewee ha bisogno di paint () (che chiama paint () su tutti i bambini - che sono membri della classe Composite di base ). Quindi non riesco davvero a visualizzarlo con la digitazione strutturale.
Izhaki,

Stai considerando un sacco di cose che in questo caso non contano del tutto.
DeadMG

Quindi probabilmente non ho ricevuto la risposta. Sarebbe bello se elaborassi.
Izhaki,

Ho fatto una modifica. La linea di fondo è che le classi di base sono assolutamente irrilevanti e nessuno se ne preoccupa. Sono solo un dettaglio di implementazione.
DeadMG

1
OK. Questo sta iniziando a dare un senso. A) So cos'è la dattilografia d'anatra. B) Non sono sicuro del motivo per cui le interfacce sono così centrali in questa risposta: la suddivisione della classe è nell'interesse della condivisione del comportamento comune, per ora puoi tranquillamente ignorare le interfacce. C) Dì nel tuo esempio che hai SVGViewee.addChild(), ma hai CanvasVieweeanche bisogno esattamente della stessa funzione. Quindi sembra logico per me sia quello inerente al composito?
Izhaki,

3

Veloce panoramica

Soluzione 3: il modello di progettazione software "Parallel Class Hierarchy" è tuo amico.

Risposta estesa estesa

Il tuo design INIZIA A DESTRA. Può essere ottimizzato, alcune classi o membri possono essere rimossi, ma l'idea di "gerarchia parallela" che stai applicando per risolvere un problema È GIUSTA.

Hanno affrontato lo stesso concetto più volte, di solito nelle gerarchie di controllo.

Dopo un po ', HO FINITO DI FARE LA STESSA SOLUZIONE DI ALTRI SVILUPPI, che a volte viene chiamato il modello di progettazione "Gerarchia parallela" o il modello di progettazione "Gerarchia doppia".

(1) Hai mai diviso una singola classe in una singola gerarchia di classi?

(2) Hai mai diviso una singola classe in più classi, senza una gerarchia?

Se hai applicato queste soluzioni precedenti, separatamente, sono un modo per risolvere alcuni problemi.

E se combinassimo queste due soluzioni contemporaneamente?

Combinali e otterrai questo "Design Pattern".

Implementazione

Ora, applichiamo il modello di progettazione software "Parallel Class Hierarchy" al tuo caso.

Al momento hai 2 o più gerarchie di classi indipendenti, che sono molto simili, hanno associazioni o scopi simili, hanno proprietà o metodi simili.

Si desidera evitare di avere codice o membri duplicati ("coerenza"), tuttavia, non è possibile unire queste classi direttamente in una singola, a causa delle differenze tra loro.

Quindi, le tue gerarchie sono molto simili a questa figura, ma, tuttavia, ce ne sono più di una:

................................................
...............+----------------+...............
...............|     Common::   |...............
...............|    Composite   |...............
...............+----------------+...............
...............|      ...       |...............
...............+-------+--------+...............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
...............+-------+--------+...............
...............|     Common::   |...............
...............|     Viewee     |...............
...............+----------------+...............
...............|      ...       |...............
...............+-------+--------+...............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..|     Common::   |........|     Common::   |..
..|     Visual     |........|   Structural   |..
..+----------------+........+----------------+..
..|      ...       |........|      ...       |..
..+----------------+........+----------------+..
................................................

Figure 1

In questo, non ancora certificato, Design Pattern, VARIE GERARCHIE SIMILI, VENGONO UNITE, IN UNA SINGOLA GERARCHIA, e ogni classe condivisa o comune viene estesa mediante sottoclasse.

Nota che questa soluzione è complessa, poiché hai già a che fare con diverse gerarchie, quindi è uno scenario complesso.

1 La classe principale

In ogni gerarchia c'è una classe "root" condivisa.

Nel tuo caso, esiste una classe "Composite" indipendente, per ciascuna gerarchia, che può avere proprietà simili e alcuni metodi simili.

Alcuni di questi membri possono essere uniti, altri non possono essere uniti.

Quindi, ciò che uno sviluppatore può fare è creare una classe radice di base e sottoclassare il caso equivalente per ciascuna gerarchia.

Nella Figura 2, puoi vedere un diagramma solo per questa classe, in cui ogni classe mantiene lo spazio dei nomi.

I membri, ormai, sono stati omessi.

................................................
...............+-------+--------+...............
...............|     Common::   |...............
...............|    Composite   |...............
...............+----------------+...............
...............|      ...       |...............
...............+-------+--------+...............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..|     Canvas::   |........|      SVG::     |..
..|    Composite   |........|    Composite   |..
..+----------------+........+----------------+..
..|      ...       |........|      ...       |..
..+----------------+........+----------------+..
................................................

Figure 2

Come puoi notare, ogni classe "composita" non è più in una gerarchia separata, ma è fusa in una singola gerarchia condivisa o comune.

Quindi, aggiungiamo i membri, quelli che sono uguali, possono essere spostati nella superclasse e quelli che sono diversi, per ogni classe di base.

E come già sapete, i metodi "virtuali" o "sovraccaricati" sono definiti nella classe base, ma sostituiti nelle sottoclassi. Come in figura 3.

................................................
.............+--------------------+.............
.............|       Common::     |.............
.............|      Composite     |.............
.............+--------------------+.............
.............| [+] void AddChild()|.............
.............+---------+----------+.............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..|     Canvas::   |........|      SVG::     |..
..|    Composite   |........|    Composite   |..
..+----------------+........+----------------+..
..|      ...       |........|      ...       |..
..+----------------+........+----------------+..
................................................

Figure 3

Nota che ci sono forse alcune classi senza membri e potresti essere tentato di rimuovere quelle classi, DONT. Sono chiamati "Classi vuote", "Classi enumerative" e altri nomi.

2 Le sottoclassi

Torniamo al primo diagramma. Ogni classe "Composite" aveva una sottoclasse "Viewee", in ciascuna gerarchia.

Il processo si ripete per ogni classe. Nota rispetto alla Figura 4, la classe "Common :: Viewee" discende dalla "Common :: Composite", ma, per semplicità, la classe "Common :: Composite" è omessa, dal diagramma.

................................................
.............+--------------------+.............
.............|       Common::     |.............
.............|       Viewee       |.............
.............+--------------------+.............
.............|        ...         |.............
.............+---------+----------+.............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..|     Canvas::   |........|      SVG::     |..
..|     Viewee     |........|     Viewee     |..
..+----------------+........+----------------+..
..|      ...       |........|      ...       |..
..+----------------+........+----------------+..
................................................

Figure 4

Noterai che "Canvas :: Viewee" e "SVG :: Viewee" NON discendono più dal rispettivo "Composito", ma dal comune "Comune :: Viewee".

Puoi aggiungere i membri, ora.

......................................................
.........+------------------------------+.............
.........|            Common::          |.............
.........|            Viewee            |.............
.........+------------------------------+.............
.........| [+] bool Validate()          |.............
.........| [+] Rect GetAbsoluteBounds() |.............
.........+-------------+----------------+.............
.......................|..............................
.......................^..............................
....................../.\.............................
.....................+-+-+............................
.......................|..............................
..........+------------+----------------+.............
..........|.............................|.............
..+-------+---------+........+----------+----------+..
..|      Canvas::   |........|         SVG::       |..
..|      Viewee     |........|        Viewee       |..
..+-----------------+........+---------------------+..
..|                 |........| [+] Viewee Element  |..
..+-----------------+........+---------------------+..
..| [+] void Paint()|........| [+] void addChild() |..
..+-----------------+........+---------------------+..
......................................................

Figure 5

3 Ripetere il processo

Il processo continuerà, per ogni classe, "Canvas :: Visual" non discenderà da "Canvas :: Viewee", l'acquisto da "Commons :: Visual", "Canvas :: Structural" non discenderà da "Canvas :: Viewee ", acquista da" Commons :: Structural "e così via.

4 Il diagramma della gerarchia 3D

Finirai per ottenere una sorta di diagramma 3D, con diversi livelli, il livello superiore, ha la gerarchia "Comune" e i livelli inferiori, ha ogni gerarchia aggiuntiva.

Le tue gerarchie di classi indipendenti originali, in cui qualcosa di simile a questo (Figura 6):

.................................................
..+-----------------+.......+-----------------+..
..|      Common::   |.......|       SVG::     |..
..|     Composite   |.......|     Composite   |..
..+-----------------+.......+-----------------+..
..|       ...       |.......|       ...       |..
..+--------+--------+.......+--------+--------+..
...........|.........................|...........
...........^.........................^...........
........../.\......................./.\..........
.........+-+-+.....................+-+-+.........
...........|.........................|...........
..+--------+--------+.......+--------+--------+..
..|      Common::   |.......|       SVG::     |..
..|      Viewee     |.......|      Viewee     |..
..+-----------------+.......+-----------------+..
..|       ...       |.......|       ...       |..
..+--------+--------+.......+--------+--------+..
...........|.........................|...........
...........^.........................^...........
........../.\......................./.\..........
.........+-+-+.....................+-+-+.........
...........|.........................|...........
..+--------+--------+.......+--------+--------+..
..|      Common::   |.......|       SVG::     |..
..|      Visual     |.......|      Visual     |..
..+-----------------+.......+-----------------+..
..|       ...       |.......|       ...       |..
..+--------+--------+.......+--------+--------+..
...........|.........................|...........
...........^.........................^...........
........../.\......................./.\..........
.........+-+-+.....................+-+-+.........
...........|.........................|...........
..+--------+--------+.......+--------+--------+..
..|      Common::   |.......|       SVG::     |..
..|       Rect      |.......|       Rect      |..
..+-----------------+.......+-----------------+..
..|       ...       |.......|       ...       |..
..+-----------------+.......+-----------------+..
.................................................

Figure 6

Si noti che alcune classi vengono omesse e l'intera gerarchia "Canvas" viene omessa, per semplicità.

La gerarchia di classi integrata finale potrebbe essere simile a questa:

.................................................
..+-----------------+.../+..+-----------------+..
..|      Common::   +--<.+--+       SVG::     |..
..|     Composite   |...\+..|     Composite   |..
..+-----------------+.......+-----------------+..
..|       ...       |.......|       ...       |..
..+--------+--------+.......+-----------------+..
...........|.....................................
...........^.....................................
........../.\....................................
.........+-+-+...................................
...........|.....................................
..+--------+--------+.../+..+-----------------+..
..|      Common::   +--<.+--+       SVG::     |..
..|      Viewee     |...\+..|      Viewee     |..
..+-----------------+.......+-----------------+..
..|       ...       |.......|       ...       |..
..+--------+--------+.......+-----------------+..
...........|.....................................
...........^.....................................
........../.\....................................
.........+-+-+...................................
...........|.....................................
..+--------+--------+.../+..+-----------------+..
..|      Common::   +--<.+--+       SVG::     |..
..|      Visual     |...\+..|      Visual     |..
..+-----------------+.......+-----------------+..
..|       ...       |.......|       ...       |..
..+--------+--------+.......+-----------------+..
...........|.....................................
...........^.....................................
........../.\....................................
.........+-+-+...................................
...........|.....................................
..+--------+--------+.../+..+-----------------+..
..|      Common::   +--<.+--+       SVG::     |..
..|       Rect      |...\+..|       Rect      |..
..+-----------------+.......+-----------------+..
..|       ...       |.......|       ...       |..
..+-----------------+.......+-----------------+..
.................................................
Figure 7

Si noti che alcune classi vengono omesse e tutte le classi "Canvas" vengono omesse, in modo semplice, ma saranno simili alle classi "SVG".

Le classi "comuni" potrebbero essere rappresentate come un singolo livello di un diagramma 3D, le classi "SVG" in un altro livello e le classi "Canvas", in un terzo livello.

Controlla che ogni livello sia correlato al primo, in cui ogni classe ha una classe genitore della gerarchia "Comune".

L'implementazione del codice potrebbe richiedere l'uso di, ereditarietà dell'interfaccia, ereditarietà delle classi o "mixin", a seconda di ciò che supporta il tuo linguaggio di programmazione.

Sommario

Come qualsiasi soluzione di programmazione, non abbiate fretta nell'ottimizzazione, l'ottimizzazione è molto importante, tuttavia, una cattiva ottimizzazione, può diventare un problema più grande del problema originale.

Non consiglio di applicare "Soluzione 1" o "Soluzione 2".

In "Soluzione 1" non si applica, perché, l'eredità, è richiesta in ciascun caso.

"Soluzione 2", "Mixin" possono essere applicati, ma, dopo aver progettato le classi e le gerarchie.

I mixin sono un'alternativa all'ereditarietà basata su interfaccia o ereditarietà multipla basata su classi.

La mia proposta, Soluzione 3, viene talvolta definita come modello di progettazione "Gerarchia parallela" o modello di progettazione "Gerarchia doppia".

Molti sviluppatori / designer non sono d'accordo e credono che non dovrebbe esistere. Ma ho usato me stesso e altri sviluppatori come una soluzione comune ai problemi, come quella della tua domanda.

Un'altra cosa mancante. Nelle tue soluzioni precedenti, il problema principale non era quello di utilizzare "mixin" o "interfacce", ma, per prima cosa, di affinare il modello delle tue classi e successivamente utilizzare una funzionalità del linguaggio di programmazione esistente.


Grazie per la risposta molto approfondita. Penso di aver capito bene, quindi lasciami chiedere: canvas.vieweee tutti i suoi discendenti hanno bisogno di un metodo chiamato paint(). Né le classi commonné ne svghanno bisogno. Ma nella tua soluzione, la gerarchia è dentro common, no canvaso svgcome nella mia soluzione 2. Quindi, come paint()finisce esattamente in tutte le sottoclassi canvas.vieweese non c'è eredità lì?
Izhaki,

@Izhaki Mi dispiace se ci sono alcuni bug nella mia risposta. Quindi paint()dovrebbe essere spostato o dichiarato in "canvas :: viewee". L'idea generale del modello rimane, ma è possibile che alcuni membri debbano essere spostati o modificati.
Umlcat,

OK, quindi come ottengono le sottoclassi, se non ne deriva nessuna canvas::viewee?
Izhaki,

Hai usato uno strumento per creare la tua arte ASCII? (Non sono sicuro che i punti aiutino davvero, per quello che vale.)
Aaron Hall

1

In un articolo intitolato Design Patterns for Dealing with Dual Inheritance Hierarchies in C ++ , lo zio Bob presenta una soluzione chiamata Stairway to Heaven . È dichiarato intento:

Questo modello descrive la rete di relazioni ereditarie necessarie quando una determinata gerarchia deve essere adattata, nella sua interezza, a un'altra classe.

E il diagramma fornito:

Un diagramma di classe con due strutture di ereditarietà parallele, in cui ogni classe a destra è anche virtualmente inerente alla sua classe gemella a sinistra.  Anche la gerarchia di sinistra si basa completamente sull'eredità virtuale

Sebbene nella soluzione 2 non ci sia eredità virtuale, è molto in accordo con il modello Stairway to Heaven . Pertanto, la soluzione 2 sembra ragionevole per questo problema.

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.