Краткая информация
Решение 3: Шаблон проектирования программного обеспечения "Параллельная классовая иерархия" - ваш друг.
Длинный расширенный ответ
Ваш дизайн НАЧИНАЕТСЯ ПРАВО. Он может быть оптимизирован, некоторые классы или члены могут быть удалены, но идея «параллельной иерархии», которую вы применяете для решения проблемы, ПРАВИЛЬНА.
Имейте дело с той же концепцией несколько раз, обычно в иерархиях управления.
Через некоторое время я закончил делать то же решение, что и другие разработчики, что иногда называют шаблоном проектирования «Параллельная иерархия» или шаблоном проектирования «Двойная иерархия».
(1) Вы когда-нибудь разделяли один класс на единую иерархию классов?
(2) Вы когда-нибудь разделяли один класс на несколько классов без иерархии?
Если вы применили эти предыдущие решения отдельно, то это способ решить некоторые проблемы.
Но что, если мы объединим эти два решения одновременно?
Объедините их, и вы получите этот «шаблон дизайна».
Реализация
Теперь давайте применим шаблон проектирования программного обеспечения «Параллельная классовая иерархия» в вашем случае.
В настоящее время у вас есть 2 или более независимых иерархий классов, которые очень похожи, имеют сходные ассоциации или намеки, имеют сходные свойства или методы.
Вы хотите избежать дублирования кода или членов («непротиворечивость»), но вы не можете объединить эти классы непосредственно в один из-за различий между ними.
Итак, ваши иерархии очень похожи на этот рисунок, но, тем не менее, их более одного:
................................................
...............+----------------+...............
...............| Common:: |...............
...............| Composite |...............
...............+----------------+...............
...............| ... |...............
...............+-------+--------+...............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
...............+-------+--------+...............
...............| Common:: |...............
...............| Viewee |...............
...............+----------------+...............
...............| ... |...............
...............+-------+--------+...............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..| Common:: |........| Common:: |..
..| Visual |........| Structural |..
..+----------------+........+----------------+..
..| ... |........| ... |..
..+----------------+........+----------------+..
................................................
Figure 1
В этом, еще не сертифицированном, шаблон проектирования, несколько одинаковых иерархий, объединены в одну иерархию, и каждый общий или общий класс расширяется подклассами.
Обратите внимание, что это решение является сложным, поскольку вы уже имеете дело с несколькими иерархиями, поэтому это сложный сценарий.
1 Корневой класс
В каждой иерархии есть общий «корневой» класс.
В вашем случае для каждой иерархии существует независимый «составной» класс, который может иметь некоторые схожие свойства и несколько похожих методов.
Некоторые из этих участников могут быть объединены, некоторые из этих участников не могут быть объединены.
Итак, что может сделать разработчик, так это создать базовый корневой класс и создать подкласс для эквивалентного случая для каждой иерархии.
На рисунке 2 вы видите диаграмму только для этого класса, в которой каждый класс хранит свое пространство имен.
Члены, опущены, к настоящему времени.
................................................
...............+-------+--------+...............
...............| Common:: |...............
...............| Composite |...............
...............+----------------+...............
...............| ... |...............
...............+-------+--------+...............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..| Canvas:: |........| SVG:: |..
..| Composite |........| Composite |..
..+----------------+........+----------------+..
..| ... |........| ... |..
..+----------------+........+----------------+..
................................................
Figure 2
Как вы можете заметить, каждый «составной» класс больше не находится в отдельной иерархии, а объединен в одну общую или общую иерархию.
Затем давайте добавим элементы, которые одинаковы, можно переместить в суперкласс, а элементы, которые отличаются, в каждый базовый класс.
И, как вы уже знаете, «виртуальные» или «перегруженные» методы определены в базовом классе, но заменены в подклассах. Как на рисунке 3.
................................................
.............+--------------------+.............
.............| Common:: |.............
.............| Composite |.............
.............+--------------------+.............
.............| [+] void AddChild()|.............
.............+---------+----------+.............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..| Canvas:: |........| SVG:: |..
..| Composite |........| Composite |..
..+----------------+........+----------------+..
..| ... |........| ... |..
..+----------------+........+----------------+..
................................................
Figure 3
Обратите внимание, что, возможно, есть некоторые классы без членов, и вы можете испытать искушение удалить эти классы, DONT. Они называются «полые классы», «перечислительные классы» и другие имена.
2 Подкласса
Вернемся к первой диаграмме. Каждый «составной» класс имел подкласс «Viewee» в каждой иерархии.
Процесс повторяется для каждого класса. Обратите внимание, что на рисунке 4 класс «Common :: Viewee» происходит от «Common :: Composite», но для простоты класс «Common :: Composite» на диаграмме опущен.
................................................
.............+--------------------+.............
.............| Common:: |.............
.............| Viewee |.............
.............+--------------------+.............
.............| ... |.............
.............+---------+----------+.............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..| Canvas:: |........| SVG:: |..
..| Viewee |........| Viewee |..
..+----------------+........+----------------+..
..| ... |........| ... |..
..+----------------+........+----------------+..
................................................
Figure 4
Вы заметите, что «Canvas :: Viewee» и «SVG :: Viewee», НЕ ДОЛЖНЫ происходить из своего соответствующего «Composite», а вместо этого из общего «Common :: Viewee».
Вы можете добавить участников, сейчас.
......................................................
.........+------------------------------+.............
.........| Common:: |.............
.........| Viewee |.............
.........+------------------------------+.............
.........| [+] bool Validate() |.............
.........| [+] Rect GetAbsoluteBounds() |.............
.........+-------------+----------------+.............
.......................|..............................
.......................^..............................
....................../.\.............................
.....................+-+-+............................
.......................|..............................
..........+------------+----------------+.............
..........|.............................|.............
..+-------+---------+........+----------+----------+..
..| Canvas:: |........| SVG:: |..
..| Viewee |........| Viewee |..
..+-----------------+........+---------------------+..
..| |........| [+] Viewee Element |..
..+-----------------+........+---------------------+..
..| [+] void Paint()|........| [+] void addChild() |..
..+-----------------+........+---------------------+..
......................................................
Figure 5
3 Повторите процесс
Процесс будет продолжен, для каждого класса «Canvas :: Visual» не будет происходить из «Canvas :: Viewee», а Buit из «Commons :: Visual», «Canvas :: Structural» не будет происходить из «Canvas :: Viewee» ", Buit от" Commons :: Structural "и так далее.
4 Трехмерная иерархическая диаграмма
Вы закончите получать вид трехмерной диаграммы с несколькими слоями, верхний слой, имеет «общую» иерархию, а нижние слои имеют каждую дополнительную иерархию.
Ваши исходные независимые иерархии классов, где что-то похожее на это (рисунок 6):
.................................................
..+-----------------+.......+-----------------+..
..| Common:: |.......| SVG:: |..
..| Composite |.......| Composite |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+--------+--------+..
...........|.........................|...........
...........^.........................^...........
........../.\......................./.\..........
.........+-+-+.....................+-+-+.........
...........|.........................|...........
..+--------+--------+.......+--------+--------+..
..| Common:: |.......| SVG:: |..
..| Viewee |.......| Viewee |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+--------+--------+..
...........|.........................|...........
...........^.........................^...........
........../.\......................./.\..........
.........+-+-+.....................+-+-+.........
...........|.........................|...........
..+--------+--------+.......+--------+--------+..
..| Common:: |.......| SVG:: |..
..| Visual |.......| Visual |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+--------+--------+..
...........|.........................|...........
...........^.........................^...........
........../.\......................./.\..........
.........+-+-+.....................+-+-+.........
...........|.........................|...........
..+--------+--------+.......+--------+--------+..
..| Common:: |.......| SVG:: |..
..| Rect |.......| Rect |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+-----------------+.......+-----------------+..
.................................................
Figure 6
Обратите внимание, что некоторые классы опущены, и вся иерархия «Canvas» опущена, для упрощения.
Конечная интегрированная иерархия классов может выглядеть примерно так:
.................................................
..+-----------------+.../+..+-----------------+..
..| Common:: +--<.+--+ SVG:: |..
..| Composite |...\+..| Composite |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+-----------------+..
...........|.....................................
...........^.....................................
........../.\....................................
.........+-+-+...................................
...........|.....................................
..+--------+--------+.../+..+-----------------+..
..| Common:: +--<.+--+ SVG:: |..
..| Viewee |...\+..| Viewee |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+-----------------+..
...........|.....................................
...........^.....................................
........../.\....................................
.........+-+-+...................................
...........|.....................................
..+--------+--------+.../+..+-----------------+..
..| Common:: +--<.+--+ SVG:: |..
..| Visual |...\+..| Visual |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+-----------------+..
...........|.....................................
...........^.....................................
........../.\....................................
.........+-+-+...................................
...........|.....................................
..+--------+--------+.../+..+-----------------+..
..| Common:: +--<.+--+ SVG:: |..
..| Rect |...\+..| Rect |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+-----------------+.......+-----------------+..
.................................................
Figure 7
Обратите внимание, что некоторые классы опущены, и целые классы «Canvas» опущены, для упрощения, но будут аналогичны классам «SVG».
«Общие» классы могут быть представлены как один слой трехмерной диаграммы, классы «SVG» на другом уровне и классы «Canvas» на третьем уровне.
Убедитесь, что каждый слой относится к первому, в котором каждый класс имеет родительский класс «общей» иерархии.
Реализация кода может потребовать использования либо наследования интерфейса, наследования классов, либо «mixins», в зависимости от того, что поддерживает ваш язык программирования.
Резюме
Как и в любом программном решении, не торопитесь с оптимизацией, оптимизация очень важна, но плохая оптимизация может стать большей проблемой, чем исходная.
Я не рекомендую применять ни «Решение 1», ни «Решение 2».
На «Решение 1» не распространяется, поскольку наследование требуется в каждом конкретном случае.
«Решение 2», «Mixins» могут быть применены, но, после проектирования классов и иерархий.
Mixins, являются альтернативой для наследования на основе интерфейса или множественного наследования на основе классов.
Мое предлагаемое решение 3 иногда называют шаблоном проектирования «Параллельная иерархия» или шаблоном проектирования «Двойная иерархия».
Многие разработчики / дизайнеры не согласятся с этим и считают, что этого не должно быть. Но я использовал miself и другие разработчики в качестве общего решения проблем, например, вашего вопроса.
Еще одна недостающая вещь. В ваших предыдущих решениях основная проблема заключалась не в том, чтобы использовать «миксины» или «интерфейсы», а в том, чтобы сначала уточнить модель ваших классов, а затем использовать существующую функцию языка программирования.