Parallele Hierarchien - teils gleich, teils unterschiedlich

12

Es gibt einige ähnliche Fragen 1 ,2 ,3 ,4 , aber nicht scheint genau der Fall in dieser Frage, noch scheinen die Lösungen optimal.

Dies ist eine allgemeine OOP-Frage, vorausgesetzt, Polymorphismus, Generika und Mixins sind verfügbar. Die tatsächlich zu verwendende Sprache ist OOP Javascript (Typescript), aber es ist das gleiche Problem in Java oder C ++.

Ich habe parallele Klassenhierarchien, die manchmal dasselbe Verhalten (Schnittstelle und Implementierung) aufweisen, aber manchmal hat jedes sein eigenes "geschütztes" Verhalten. Illustriert wie folgt:

3 parallele Klassenhierarchien, die mittlere Spalte zeigt die gemeinsamen Teile, die linke Spalte ist die Canvas-Hierarchie und die rechte Spalte zeigt die SVG-Hierarchie

Dies dient nur zu Illustrationszwecken . Es ist nicht das eigentliche Klassendiagramm. Um es zu lesen:

  • Alles in der gemeinsamen Hierarchie (Mitte) wird zwischen der Canvas-Hierarchie (links) und der SVG-Hierarchie (rechts) geteilt. Mit Teilen meine ich sowohl Schnittstelle als auch Implementierung.
  • Alles, was sich nur in der linken oder rechten Spalte befindet, bedeutet ein Verhalten (Methoden und Elemente), das für diese Hierarchie spezifisch ist. Beispielsweise:
    • Sowohl die linke als auch die rechte Hierarchie verwenden genau dieselben Validierungsmechanismen, die als einzelne Methode ( Viewee.validate()) in der gemeinsamen Hierarchie dargestellt werden.
    • Nur die Canvas-Hierarchie hat eine Methode paint(). Diese Methode ruft die Malmethode für alle untergeordneten Elemente auf.
    • Die SVG-Hierarchie muss die addChild()Methode von überschreiben Composite, dies ist jedoch bei der Canvas-Hierarchie nicht der Fall.
  • Die Konstrukte aus den beiden Seitenhierarchien können nicht gemischt werden. Eine Fabrik sorgt dafür.

Lösung I - Vererbung auseinander ziehen

Fowlers Tease Apart Inheritance scheint hier nicht den Job zu machen, da es einige Diskrepanzen zwischen den beiden Parallelen gibt.

Lösung II - Mixins

Dies ist die einzige, an die ich derzeit denken kann. Die beiden Hierarchien werden separat entwickelt, aber auf jeder Ebene mischen die Klassen die gemeinsame Klasse, die nicht Teil einer Klassenhierarchie ist. Das Weglassen der structuralGabel sieht folgendermaßen aus:

Die drei Spalten, die linke und die rechte Spalte, sind parallele Hierarchien, wobei jede Klasse auch einer gemeinsamen Klasse innewohnt.  Die allgemeinen Klassen sind nicht Teil einer Hierarchie

Beachten Sie, dass sich jede Spalte in einem eigenen Namespace befindet, damit Klassennamen nicht in Konflikt geraten.

Die Frage

Kann jemand Fehler mit diesem Ansatz sehen? Kann sich jemand eine bessere Lösung vorstellen?


Nachtrag

Hier ist ein Beispielcode, wie dieser verwendet werden soll. Der Namespace svgkann ersetzt werden durch 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 );

Im Wesentlichen erstellen Clients zur Laufzeit eine Instanzhierarchie von Viewee-Unterklassen. wie so:

Ein Bild, das eine Hierarchie von Objekten wie Layer, Rect, Scroller usw. zeigt.

Angenommen, alle diese Viewees stammen aus der Canvas-Hierarchie. Sie werden durch Durchlaufen der Hierarchie gerendert, die paint()jeden Viewee aufrufen kann. Wenn sie aus der SVG-Hierarchie stammen, wissen die Betrachter, wie sie sich dem DOM hinzufügen können, aber es gibt keine paint()Durchquerung.

Izhaki
quelle
Literaturempfehlung: Design Review: zum Thema oder nicht?
Robert Harvey
Vielleicht versuchen Sie es mit einem voll ausgestatteten Decorator-Designmuster (Erich Gamma et al., Design Patterns)?
Zon
Was ist ein Viewee? Was bedeutet "parallel" als Substantiv (im Gegensatz zu einem Adjektiv)?
Tulains Córdova
Haben Sie Mehrfachvererbung?
Tulains Córdova
Enthalten Canvas- oder SVG-Klassen zusätzliche Status oder Daten, die nicht gemeinsam sind? Wie benutzt du die Klassen? Können Sie einen Beispielcode zeigen, der zeigt, wie diese Hiearchies verwendet werden können?
Euphoric

Antworten:

5

Der zweite Ansatz trennt Schnittstellen besser nach dem Prinzip der Schnittstellentrennung.

Ich würde jedoch eine Paintable-Oberfläche hinzufügen.

Ich würde auch einige Namen ändern. Keine Notwendigkeit, Verwirrung zu stiften:

// 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 {
}
Tulains Córdova
quelle
Ich denke, Schnittstellen können in diesem Fall hilfreich sein. Ich würde gerne wissen, warum Ihre Antwort abgelehnt wurde.
Umlcat
Nicht der Downvoter, aber IMHO exponentielle Schnittstellen sind kein gutes Muster
Dagnelies
@arnaud was meinst du mit "exponentiellen Schnittstellen"?
Tulains Córdova
@ user61852 ... nun, sagen wir einfach, es sind viele Schnittstellen. "exponentiell" war eigentlich ein falscher Begriff, es ist eher wie "multiplikativ". In dem Sinne, dass Sie, wenn Sie mehr "Facetten" (zusammengesetzt, visuell, übermalbar ...) und mehr "Elemente" (Leinwand, SVG ...) hätten, am Ende viele Schnittstellen hätten.
Dagnelies
@arnaud Du hast einen Punkt, aber zumindest gibt es vorher ein flexibles Design und OPs Albtraum der Vererbung wäre gelöst, wenn du dich nicht gezwungen fühlst, zu verlängern. Sie erweitern eine Klasse, wenn Sie möchten und nicht durch eine erfundene Hierarchie gezwungen werden.
Tulains Córdova
3

Dies ist eine allgemeine OOP-Frage, vorausgesetzt, Polymorphismus, Generika und Mixins sind verfügbar. Die tatsächlich zu verwendende Sprache ist OOP Javascript (Typescript), aber es ist das gleiche Problem in Java oder C ++.

Das stimmt eigentlich gar nicht. Typoskript hat einen wesentlichen Vorteil gegenüber Java, nämlich die strukturelle Typisierung. Sie können in C ++ etwas Ähnliches mit Vorlagen vom Typ Ente tun, aber es ist viel aufwändiger.

Definieren Sie grundsätzlich Ihre Klassen, aber erweitern oder definieren Sie keine Schnittstellen. Definieren Sie dann einfach die Schnittstelle, die Sie benötigen, und nehmen Sie sie als Parameter. Dann können die Objekte mit dieser Schnittstelle übereinstimmen - sie müssen es nicht wissen, um sie im Voraus zu erweitern. Jede Funktion kann genau und nur die Bits deklarieren, über die sie einen Scheiß geben, und der Compiler gibt Ihnen einen Pass, wenn der endgültige Typ ihn erfüllt, obwohl die Klassen diese Schnittstellen nicht explizit erweitern.

Dies befreit Sie von der Notwendigkeit, tatsächlich eine Schnittstellenhierarchie zu definieren und zu definieren, welche Klassen welche Schnittstellen erweitern sollen.

Definieren Sie einfach jede Klasse und vergessen Sie die Schnittstellen - die strukturelle Typisierung wird sich darum kümmern.

Beispielsweise:

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

Dies ist ein absolut legitimes Typoskript. Beachten Sie, dass die konsumierenden Funktionen nichts über die Basisklassen oder Schnittstellen wissen oder geben, die bei der Definition der Klassen verwendet werden.

Es spielt keine Rolle, ob die Klassen verwandt sind oder nicht. Es spielt keine Rolle, ob sie Ihre Schnittstelle erweitert haben. Definieren Sie einfach die Schnittstelle als Parameter, und fertig - alle Klassen, die sie erfüllen, werden akzeptiert.

DeadMG
quelle
Es klingt vielversprechend, aber ich verstehe den Vorschlag nicht wirklich (sorry, möglicherweise zu viel OOP-Voreingenommenheit). Vielleicht können Sie ein Codebeispiel teilen? Beispielsweise benötigen sowohl svg.Viewee als auch canvas.Viewee eine validate()Methode (deren Implementierung für beide identisch ist). dann nur svg.Viewee muss außer Kraft setzen addChild () , während nur canvas.Viewee braucht paint () (die Anrufe () Lack auf alle Kinder - , die ein Mitglied der Base sind Composite - Klasse). Ich kann mir das also nicht wirklich mit struktureller Typisierung vorstellen.
Izhaki
Sie erwägen eine ganze Reihe von Dingen, die in diesem Fall einfach keine Rolle spielen.
DeadMG
Also habe ich wahrscheinlich überhaupt keine Antwort bekommen. Wäre schön, wenn Sie näher darauf eingehen.
Izhaki
Ich habe eine Bearbeitung vorgenommen. Das Fazit ist, dass die Basisklassen absolut irrelevant sind und sich niemand um sie kümmert. Sie sind nur ein Implementierungsdetail.
DeadMG
1
IN ORDNUNG. Das macht langsam Sinn. A) Ich weiß, was Entenschreiben ist. B) Ich bin mir nicht sicher, warum Schnittstellen in dieser Antwort so zentral sind. Die Aufschlüsselung der Klassen dient dem gemeinsamen Verhalten. Sie können Schnittstellen vorerst ignorieren. C) Sagen Sie in Ihrem Beispiel SVGViewee.addChild(), CanvasVieweedass Sie genau dieselbe Funktion benötigen. Scheint mir also Sinn zu machen, dass beide von Composite inhärent sind?
Izhaki
3

Schneller Überblick

Lösung 3: Das Software-Entwurfsmuster "Parallele Klassenhierarchie" ist Ihr Freund.

Lange erweiterte Antwort

Ihr Design begann richtig. Es kann optimiert werden, einige Klassen oder Mitglieder können entfernt werden, aber die Idee der "parallelen Hierarchie", die Sie anwenden, um ein Problem zu lösen, ist RICHTIG.

Beschäftige dich mehrmals mit demselben Konzept, normalerweise in Kontrollhierarchien.

Nach einer Weile beendete ich die gleiche Lösung wie andere Entwickler, die manchmal als "Parallel Hierarchy" -Designmuster oder "Dual Hierarchy" -Designmuster bezeichnet wird.

(1) Haben Sie jemals eine einzelne Klasse in eine einzelne Hierarchie von Klassen aufgeteilt?

(2) Haben Sie jemals eine einzelne Klasse in mehrere Klassen ohne Hierarchie aufgeteilt?

Wenn Sie diese vorherigen Lösungen separat angewendet haben, können Sie damit einige Probleme lösen.

Was aber, wenn wir diese beiden Lösungen gleichzeitig kombinieren?

Kombinieren Sie sie und Sie erhalten dieses "Designmuster".

Implementierung

Wenden wir nun das Software-Entwurfsmuster "Parallele Klassenhierarchie" auf Ihren Fall an.

Sie haben derzeit zwei oder mehr unabhängige Hierarchien von Klassen, die sehr ähnlich sind, ähnliche Assoziationen oder Absichten haben, ähnliche Eigenschaften oder Methoden haben.

Sie möchten vermeiden, dass Code oder Mitglieder dupliziert werden ("Konsistenz"), können diese Klassen jedoch aufgrund der Unterschiede zwischen ihnen nicht direkt zu einer einzigen zusammenführen.

Ihre Hierarchien sind dieser Abbildung also sehr ähnlich, es gibt jedoch mehr als eine:

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

Figure 1

In diesem noch nicht zertifizierten Entwurfsmuster werden MEHRERE ÄHNLICHE HIERARCHIEN ZU EINER EINZELNEN HIERARCHIE zusammengeführt, und jede gemeinsame oder gemeinsame Klasse wird durch Unterklassen erweitert.

Beachten Sie, dass diese Lösung komplex ist, da Sie bereits mit mehreren Hierarchien zu tun haben. Daher handelt es sich um ein komplexes Szenario.

1 Die Wurzelklasse

In jeder Hierarchie gibt es eine gemeinsame "Root" -Klasse.

In Ihrem Fall gibt es für jede Hierarchie eine unabhängige "Composite" -Klasse, die ähnliche Eigenschaften und ähnliche Methoden aufweisen kann.

Einige dieser Mitglieder können zusammengeführt werden, andere können nicht zusammengeführt werden.

Ein Entwickler kann also eine Basisstammklasse erstellen und den entsprechenden Fall für jede Hierarchie unterordnen.

In Abbildung 2 sehen Sie ein Diagramm nur für diese Klasse, in dem jede Klasse ihren Namespace behält.

Die Mitglieder sind inzwischen weggelassen.

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

Figure 2

Wie Sie vielleicht bemerken, befindet sich jede "zusammengesetzte" Klasse nicht mehr in einer separaten Hierarchie, sondern wird zu einer einzigen gemeinsamen oder gemeinsamen Hierarchie zusammengeführt.

Fügen wir dann die Mitglieder hinzu, die gleich sind, die in die Oberklasse verschoben werden können, und diejenigen, die unterschiedlich sind, zu jeder Basisklasse.

Und wie Sie bereits wissen, werden "virtuelle" oder "überladene" Methoden in der Basisklasse definiert, aber in den Unterklassen ersetzt. Wie Abbildung 3.

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

Figure 3

Beachten Sie, dass es möglicherweise einige Klassen ohne Mitglieder gibt und Sie möglicherweise versucht sind, diese Klassen zu entfernen, NICHT. Sie werden "Hollow Classes", "Enumerative Classes" und andere Namen genannt.

2 Die Unterklassen

Kehren wir zum ersten Diagramm zurück. Jede "Composite" -Klasse hatte in jeder Hierarchie eine "Viewee" -Unterklasse.

Der Vorgang wird für jede Klasse wiederholt. Beachten Sie, dass in Abbildung 4 die Klasse "Common :: Viewee" von "Common :: Composite" abstammt, der Einfachheit halber jedoch die Klasse "Common :: Composite" im Diagramm weggelassen wird.

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

Figure 4

Sie werden feststellen, dass "Canvas :: Viewee" und "SVG :: Viewee" NICHT MEHR von ihrem jeweiligen "Composite" abstammen, sondern stattdessen von dem gemeinsamen "Common :: Viewee".

Sie können die Mitglieder jetzt hinzufügen.

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

Figure 5

3 Wiederholen Sie den Vorgang

Der Prozess wird fortgesetzt. Für jede Klasse wird "Canvas :: Visual" nicht von "Canvas :: Viewee" abgeleitet, sondern von "Commons :: Visual". "Canvas :: Structural" wird nicht von "Canvas :: Viewee" abgeleitet ", buit von" Commons :: Structural "und so weiter.

4 Das 3D-Hierarchiediagramm

Sie erhalten eine Art 3D-Diagramm mit mehreren Ebenen. Die oberste Ebene hat die Hierarchie "Allgemein" und die unterste Ebene jede zusätzliche Hierarchie.

Ihre ursprünglichen unabhängigen Klassenhierarchien, in denen dies ähnlich ist (Abbildung 6):

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

Figure 6

Beachten Sie, dass einige Klassen weggelassen werden und die gesamte "Canvas" -Hierarchie der Einfachheit halber weggelassen wird.

Die endgültige integrierte Klassenhierarchie kann ungefähr so ​​aussehen:

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

Beachten Sie, dass einige Klassen weggelassen werden und die gesamten "Canvas" -Klassen einfach weggelassen werden, jedoch den "SVG" -Klassen ähnlich sind.

Die "Common" -Klassen können als einzelne Ebene eines 3D-Diagramms dargestellt werden, die "SVG" -Klassen in einer anderen Ebene und die "Canvas" -Klassen in einer dritten Ebene.

Überprüfen Sie, ob jede Ebene mit der ersten verknüpft ist, in der jede Klasse eine übergeordnete Klasse der "Common" -Hierarchie hat.

Für die Code-Implementierung müssen möglicherweise entweder Schnittstellenvererbung, Klassenvererbung oder "Mixins" verwendet werden, je nachdem, was Ihre Programmiersprache unterstützt.

Zusammenfassung

Wie bei jeder Programmierlösung ist die Optimierung sehr wichtig. Eine schlechte Optimierung kann jedoch zu einem größeren Problem werden als das ursprüngliche Problem.

Ich empfehle weder "Lösung 1" noch "Lösung 2" anzuwenden.

In "Lösung 1" gilt dies nicht, da jeweils die Vererbung erforderlich ist.

"Lösung 2", "Mixins" können angewendet werden, jedoch nach dem Entwerfen der Klassen und Hierarchien.

Mixins sind eine Alternative für die schnittstellenbasierte Vererbung oder die klassenbasierte Mehrfachvererbung.

Meine vorgeschlagene Lösung 3 wird manchmal als Entwurfsmuster "Parallele Hierarchie" oder Entwurfsmuster "Doppelte Hierarchie" bezeichnet.

Viele Entwickler / Designer werden dem nicht zustimmen und glauben, dass es nicht existieren sollte. Aber ich habe mich selbst und andere Entwickler als gemeinsame Lösung für Probleme verwendet, wie die Ihrer Frage.

Noch etwas fehlt. In Ihren vorherigen Lösungen bestand das Hauptproblem nicht darin, "Mixins" oder "Interfaces" zu verwenden, sondern zunächst das Modell Ihrer Klassen zu verfeinern und später eine vorhandene Programmiersprachenfunktion zu verwenden.

umlcat
quelle
Danke für die sehr gründliche Antwort. Ich glaube, ich habe es gut verstanden, also lassen Sie mich das fragen: canvas.vieweeund alle seine Nachkommen brauchen eine Methode namens paint(). Weder die commonnoch die svgKlassen brauchen es. Aber in Ihrer Lösung befindet sich die Hierarchie in common, nicht canvasoder svgwie in meiner Lösung 2. Wie genau paint()endet es also in allen Unterklassen, canvas.vieweewenn dort keine Vererbung vorhanden ist?
Izhaki
@Izhaki Entschuldigung, wenn meine Antwort ein paar Fehler enthält. Dann paint()sollte in "canvas :: viewee" verschoben oder deklariert werden. Die allgemeine Musteridee bleibt erhalten, aber einige Mitglieder müssen möglicherweise verschoben oder geändert werden.
Umlcat
OK, wie bekommen es die Unterklassen, wenn keine von abgeleitet sind canvas::viewee?
Izhaki
Haben Sie ein Werkzeug verwendet, um Ihre ASCII-Kunst zu erstellen? (Ich bin nicht sicher, ob die Punkte tatsächlich helfen, was es wert ist.)
Aaron Hall
1

In einem Artikel mit dem Titel Entwurfsmuster für den Umgang mit Hierarchien mit doppelter Vererbung in C ++ stellt Onkel Bob eine Lösung namens Stairway to Heaven vor . Es ist erklärte Absicht:

Dieses Muster beschreibt das Netzwerk von Vererbungsbeziehungen, das erforderlich ist, wenn eine bestimmte Hierarchie in ihrer Gesamtheit an eine andere Klasse angepasst werden muss.

Und das Diagramm zur Verfügung gestellt:

Ein Klassendiagramm mit zwei parallelen Vererbungsstrukturen, wobei jede Klasse rechts auch ihrer Zwillingsklasse links praktisch eigen ist.  Die linke Hierarchie basiert ebenfalls vollständig auf der virtuellen Vererbung

Obwohl in Lösung 2 keine virtuelle Vererbung vorhanden ist, entspricht sie weitgehend dem Muster „ Treppe zum Himmel“ . Somit erscheint Lösung 2 für dieses Problem vernünftig.

Izhaki
quelle