Hiérarchies parallèles - en partie identiques, en partie différentes


12

Il y a pas mal de questions similaires 1 ,2 ,3 ,4 , mais non ne semble pas exactement le cas dans cette question, et les solutions ne semblent pas optimales non plus.

Il s'agit d'une question OOP générale, en supposant que le polymorphisme, les génériques et les mixins sont disponibles. Le langage réel à utiliser est OOP Javascript (Typescript), mais c'est le même problème en Java ou C ++.

J'ai des hiérarchies de classes parallèles, qui partagent parfois le même comportement (interface et implémentation), mais parfois chacune a son propre comportement «protégé». Illustré comme ceci:

3 hiérarchies de classes parallèles, la colonne centrale montre les parties communes, la colonne de gauche est la hiérarchie du canevas et la colonne de droite montre la hiérarchie SVG

Ceci est uniquement à des fins d'illustration ; ce n'est pas le diagramme de classe réel. Pour le lire:

  • Tout élément de la hiérarchie commune (centre) est partagé entre les hiérarchies Canvas (gauche) et SVG (droite). Par partage, j'entends à la fois l'interface et la mise en œuvre.
  • Rien que dans les colonnes de gauche ou de droite signifie un comportement (méthodes et membres) spécifique à cette hiérarchie. Par exemple:
    • Les hiérarchies gauche et droite utilisent exactement les mêmes mécanismes de validation, présentés comme une méthode unique ( Viewee.validate()) dans la hiérarchie commune.
    • Seule la hiérarchie du canevas a une méthode paint(). Cette méthode appelle la méthode paint sur tous les enfants.
    • La hiérarchie SVG doit remplacer la addChild()méthode de Composite, mais ce n'est pas le cas avec la hiérarchie du canevas.
  • Les constructions des hiérarchies des deux côtés ne peuvent pas être mélangées. Une usine assure cela.

Solution I - Tease Apart Héritage

Fowler's Tease Apart Inheritance ne semble pas faire l'affaire ici, car il y a une certaine différence entre les deux parallèles.

Solution II - Mixins

C'est le seul auquel je pense actuellement. Les deux hiérarchies sont développées séparément, mais à chaque niveau les classes se mélangent dans la classe commune, qui ne font pas partie d'une hiérarchie de classe. Omettre la structuralfourche, ressemblera à ceci:

Les trois colonnes à nouveau, les colonnes gauche et droite sont des hiérarchies parallèles, où chaque classe est également inhérente à une classe commune.  Les classes communes ne font pas partie d'une hiérarchie

Notez que chaque colonne sera dans son propre espace de noms, donc les noms de classe ne seront pas en conflit.

La question

Quelqu'un peut-il voir les défauts de cette approche? Quelqu'un peut-il penser à une meilleure solution?


Addenda

Voici un exemple de code d'utilisation. L'espace de noms svgpeut être remplacé par 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 );

Essentiellement, lors de l' exécution, les clients composent la hiérarchie des instances des sous-classes Viewee; ainsi:

Une image montrant une hiérarchie d'objets comme calque, rect, scroller, etc.

Supposons que tous ces visiteurs proviennent de la hiérarchie du canevas, ils sont rendus en parcourant la hiérarchie peut faire appel paint()à chaque visiteur. S'ils proviennent de la hiérarchie svg, les visiteurs savent comment s'ajouter au DOM, mais il n'y a pas de paint()traversée.



Essayez peut-être un modèle de conception de décorateur complet (Erich Gamma et als, Design Patterns)?
Zon

Qu'est-ce qu'un spectateur? Que signifie "parallèle" en tant que nom (par opposition à un adjectif)?
Tulains Córdova

Avez-vous un héritage multiple?
Tulains Córdova

Les classes Canvas ou SVG contiennent-elles des états ou des données supplémentaires qui ne sont pas communs? Comment utilisez-vous les cours? Pouvez-vous montrer un exemple de code montrant comment ces hiearchies pourraient être utilisées?
Euphoric

Réponses:


5

La deuxième approche isole mieux les interfaces, en suivant le principe de ségrégation des interfaces.

Cependant, j'ajouterais une interface Paintable.

Je changerais également certains noms. Pas besoin de créer de confusion:

// 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 {
}

Je pense que les interfaces peuvent aider dans ce cas. J'aimerais connaître les raisons pour lesquelles votre réponse a été rejetée.
umlcat

pas le downvoter, mais les interfaces exponentielles à
mon humble avis

@arnaud qu'entendez-vous par "interfaces exponentielles"?
Tulains Córdova

@ user61852 ... eh bien, disons que c'est beaucoup d'interfaces. "exponentiel" était en fait un mauvais terme, il ressemble plus à "multiplicatif". En ce sens que si vous aviez plus de "facettes" (composite, visuel, à peindre ...) et plus d '"éléments" (canvas, svg ...), vous vous retrouveriez avec beaucoup d'interfaces.
dagnelies

@arnaud Vous avez raison, mais au moins il y a une conception flexible à l'avance et le cauchemar d'héritage d'OP serait résolu lorsque vous ne vous sentez pas obligé de prolonger. Vous étendez une classe si vous le souhaitez et non forcé par une hiérarchie artificielle.
Tulains Córdova

3

Il s'agit d'une question OOP générale, en supposant que le polymorphisme, les génériques et les mixins sont disponibles. Le langage réel à utiliser est OOP Javascript (Typescript), mais c'est le même problème en Java ou C ++.

Ce n'est en fait pas vrai du tout. Le typage a un avantage substantiel sur Java, à savoir le typage structurel. Vous pouvez faire quelque chose de similaire en C ++ avec des modèles de type canard, mais c'est beaucoup plus d'efforts.

Fondamentalement, définissez vos classes, mais ne vous embêtez pas à étendre ou à définir des interfaces. Définissez ensuite simplement l'interface dont vous avez besoin et prenez-la comme paramètre. Ensuite, les objets peuvent correspondre à cette interface - ils n'ont pas besoin de savoir pour l'étendre à l'avance. Chaque fonction peut déclarer exactement et uniquement les bits pour lesquels elle donne une merde, et le compilateur vous donnera une passe si le type final le rencontre, même si les classes n'étendent pas réellement ces interfaces explicitement.

Cela vous libère de la nécessité de définir réellement une hiérarchie d'interface et de définir quelles classes doivent étendre quelles interfaces.

Définissez simplement chaque classe et oubliez les interfaces - le typage structurel s'en chargera.

Par exemple:

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());

C'est typographique totalement légitime. Notez que les fonctions consommatrices ne connaissent pas ou ne donnent pas une seule merde sur les classes de base ou les interfaces utilisées dans la définition des classes.

Peu importe que les classes soient liées ou non par héritage. Peu importe s'ils ont étendu votre interface. Définissez simplement l'interface comme paramètre, et vous avez terminé - toutes les classes qui y répondent sont acceptées.


Cela semble prometteur, mais je ne comprends pas vraiment la proposition (désolé, peut-être trop de biais OOP). Peut-être pouvez-vous partager un exemple de code? Par exemple, svg.Viewee et canvas.Viewee ont besoin d'une validate()méthode (dont l'implémentation est identique pour les deux); alors seulement svg.Viewee a besoin de remplacer addChild () , tandis que seulement canvas.Viewee a besoin de paint () (qui appelle paint () sur tous les enfants - qui sont membres de la classe Composite de base ). Je ne peux donc pas vraiment visualiser cela avec un typage structurel.
Izhaki

Vous envisagez tout un tas de choses qui n'ont absolument aucune importance dans ce cas.
DeadMG

Je n'ai donc probablement pas du tout obtenu la réponse. Ce serait bien si vous développiez.
Izhaki

J'ai fait un montage. L'essentiel est que les classes de base ne sont absolument pas pertinentes et que personne ne s'en soucie. Ils ne sont qu'un détail d'implémentation.
DeadMG

1
D'ACCORD. Cela commence à avoir un sens. A) Je sais ce qu'est la frappe de canard. B) Je ne sais pas pourquoi les interfaces sont si centrales dans cette réponse - la répartition des classes sert à partager un comportement commun, vous pouvez ignorer les interfaces en toute sécurité pour l'instant. C) Dites dans votre exemple que vous avez SVGViewee.addChild(), mais avez CanvasVieweeégalement besoin exactement de la même fonctionnalité. Il me semble donc logique que les deux soient inhérents au composite?
Izhaki

3

Rapide vue d'ensemble

Solution 3: le modèle de conception de logiciel "Hiérarchie de classes parallèles" est votre ami.

Réponse longue et étendue

Votre conception A COMMENCÉ À DROITE. Il peut être optimisé, certaines classes ou certains membres peuvent être supprimés, mais l'idée de «hiérarchie parallèle» que vous appliquez pour résoudre un problème EST DROITE.

Traitez plusieurs fois le même concept, généralement dans les hiérarchies de contrôle.

Après un certain temps, J'AI FINI DE FAIRE LA MÊME SOLUTION QUE LES AUTRES DÉVELOPPEURS, ce qu'on appelle parfois le modèle de conception "Hiérarchie parallèle" ou le modèle de conception "Double hiérarchie".

(1) Avez-vous déjà divisé une seule classe en une seule hiérarchie de classes?

(2) Avez-vous déjà divisé une seule classe en plusieurs classes, sans hiérarchie?

Si vous avez appliqué ces solutions précédentes séparément, elles sont un moyen de résoudre certains problèmes.

Mais que se passe-t-il si nous combinons ces deux solutions simultanément?

Combinez-les et vous obtiendrez ce "modèle de conception".

la mise en oeuvre

Maintenant, appliquons le modèle de conception de logiciel "Hiérarchie de classes parallèles" à votre cas.

Vous avez actuellement 2 hiérarchies de classes indépendantes ou plus, qui sont très similaires, ont des associations ou purpouse similaires, ont des propriétés ou des méthodes similaires.

Vous souhaitez éviter d'avoir du code ou des membres en double ("cohérence"), mais vous ne pouvez pas fusionner directement ces classes en une seule, en raison des différences entre elles.

Donc, vos hiérarchies sont très similaires à ce chiffre, mais, pourtant, il y en a plus d'un:

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

Figure 1

Dans ce modèle de conception non encore certifié, PLUSIEURS HIÉRARCHIES SIMILAIRES SONT FUSIONNÉES, EN UNE HIERARCHIE UNIQUE, et chaque classe partagée ou commune est étendue par sous-classement.

Notez que cette solution est complexe, car vous avez déjà affaire à plusieurs hiérarchies, c'est donc un scénario complexe.

1 La classe racine

Dans chaque hiérarchie, il existe une classe "racine" partagée.

Dans votre cas, il existe une classe "Composite" indépendante, pour chaque hiérarchie, qui peut avoir des propriétés similaires et des méthodes similaires.

Certains de ces membres peuvent être fusionnés, certains de ces membres ne peuvent pas être fusionnés.

Ainsi, ce qu'un développeur peut faire, c'est de créer une classe racine de base et de sous-classer le cas équivalent pour chaque hiérarchie.

Dans la figure 2, vous pouvez voir un diagramme uniquement pour cette classe, dans lequel chaque classe conserve son espace de noms.

Les membres sont désormais omis.

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

Figure 2

Comme vous pouvez le remarquer, chaque classe "Composite" n'est plus dans une hiérarchie distincte, mais fusionnée en une seule hiérarchie partagée ou commune.

Ensuite, ajoutons les membres, ceux qui sont les mêmes, peuvent être déplacés vers la superclasse, et ceux qui sont différents, dans chaque classe de base.

Et comme vous le savez déjà, les méthodes "virtuelles" ou "surchargées" sont définies dans la classe de base, mais remplacées dans les sous-classes. Comme la figure 3.

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

Figure 3

Notez qu'il existe peut-être des classes sans membres et, vous pourriez être tenté de supprimer ces classes, DONT. Ils sont appelés "classes creuses", "classes énumératives" et autres noms.

2 Les sous-classes

Revenons au premier diagramme. Chaque classe "Composite", avait une sous-classe "Viewee", dans chaque hiérarchie.

Le processus est répété pour chaque classe. Notez que la figure 4, la classe "Common :: Viewee" descend de la "Common :: Composite", mais, pour simplifier, la classe "Common :: Composite" est omise du diagramme.

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

Figure 4

Vous remarquerez que "Canvas :: Viewee" et "SVG :: Viewee", NE DESCEND PAS PLUS LONGTEMPS de leur "Composite" respectif, mais, du "Common :: Viewee" commun, à la place.

Vous pouvez maintenant ajouter les membres.

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

Figure 5

3 Répétez le processus

Le processus continuera, pour chaque classe, "Canvas :: Visual" ne descendra pas de "Canvas :: Viewee", buit de "Commons :: Visual", "Canvas :: Structural" ne descendra pas de "Canvas :: Viewee ", extrait de" Commons :: Structural ", et ainsi de suite.

4 Le diagramme de la hiérarchie 3D

Vous finirez d'obtenir une sorte de diagramme 3D, avec plusieurs couches, la couche supérieure, a la hiérarchie "commune" et les couches inférieures, a chaque hiérarchie supplémentaire.

Vos hiérarchies de classes indépendantes d'origine, où quelque chose de similaire à cela (figure 6):

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

Figure 6

Notez que certaines classes sont omises, et toute la hiérarchie "Canvas" est omise, pour simplifier.

La hiérarchie de classe intégrée finale peut ressembler à ceci:

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

Notez que certaines classes sont omises, et toutes les classes "Canvas" sont omises, pour simplifier, mais, seront similaires aux classes "SVG".

Les classes "Common" pourraient être représentées comme une seule couche d'un diagramme 3D, les classes "SVG" dans une autre couche et les classes "Canvas", dans une troisième couche.

Vérifiez que chaque couche est liée à la première, dans laquelle chaque classe a une classe parente de la hiérarchie "Common".

L'implémentation du code peut nécessiter l'utilisation de l'héritage d'interface, de l'héritage de classe ou de "mixins", selon ce que votre langage de programmation prend en charge.

Résumé

Comme toute solution de programmation, ne vous précipitez pas dans l'optimisation, l'optimisation est très importante, mais une mauvaise optimisation peut devenir un problème plus important que le problème d'origine.

Je ne recommande pas d'appliquer "Solution 1" ou "Solution 2".

Dans "Solution 1" ne s'applique pas, car l'héritage est requis dans chaque cas.

"Solution 2", "Mixins" peuvent être appliqués, mais, après la conception des classes et des hiérarchies.

Les mixins sont une alternative à l'héritage basé sur une interface ou à l'héritage multiple basé sur une classe.

Ma proposition, la Solution 3, est parfois appelée le modèle de conception "Hiérarchie parallèle" ou le modèle de conception "Hiérarchie double".

De nombreux développeurs / concepteurs ne seront pas d'accord avec cela et pensent qu'il ne devrait pas exister. Mais, j'ai utilisé par moi-même et d'autres développeurs comme une solution commune aux problèmes, comme celui de votre question.

Une autre chose manquante. Dans vos solutions précédentes, le problème principal n'était pas de savoir si utiliser des «mixins» ou des «interfaces», mais, pour affiner, d'abord, le modèle de vos classes, puis utiliser une fonctionnalité de langage de programmation existante.


Merci pour la réponse très complète. Je pense que je l'ai bien compris, alors laissez-moi vous demander ceci: canvas.vieweeet tous ses descendants ont besoin d'une méthode appelée paint(). Ni le commonni les svgclasses n'en ont besoin. Mais dans votre solution, la hiérarchie est dans common, pas canvasou svgcomme dans ma solution 2. Alors, comment paint()se retrouve- t-il exactement dans toutes les sous-classes canvas.viewees'il n'y a pas d'héritage?
Izhaki

@Izhaki Désolé s'il y a quelques bugs dans ma réponse. Ensuite, paint()doit être déplacé ou déclaré dans "canvas :: viewee". L'idée de modèle général reste, mais certains membres peuvent devoir être déplacés ou modifiés.
umlcat

OK, alors comment les sous-classes l'obtiennent-elles, si aucune ne dérive de canvas::viewee?
Izhaki

Avez-vous utilisé un outil pour créer votre art ascii? (Je ne suis pas sûr que les points aident vraiment, pour ce que ça vaut.)
Aaron Hall

1

Dans un article intitulé Design Patterns for Dealing with Dual Inheritance Hierarchies in C ++ , Uncle Bob présente une solution appelée Stairway to Heaven . C'est l'intention déclarée:

Ce modèle décrit le réseau de relations d'héritage qui est nécessaire lorsqu'une hiérarchie donnée doit être adaptée, dans son intégralité, à une autre classe.

Et le diagramme fourni:

Un diagramme de classes avec deux structures d'héritage parallèles, où chaque classe à droite est également pratiquement inhérente à sa classe jumelle à gauche.  La hiérarchie de gauche est également entièrement basée sur l'héritage virtuel

Bien que dans la solution 2, il n'y ait pas d'héritage virtuel, il est tout à fait conforme au modèle Stairway to Heaven . La solution 2 semble donc raisonnable pour ce problème.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.