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.