Visão geral rápida
Solução 3: O padrão de design de software "Hierarquia de classes paralelas" é seu amigo.
Resposta prolongada
Seu design COMEÇOU CERTO. Ele pode ser otimizado, algumas classes ou membros podem ser removidos, mas a idéia da "hierarquia paralela" que você está aplicando para resolver um problema É CERTA.
Lide com o mesmo conceito várias vezes, geralmente em hierarquias de controle.
Depois de um tempo, eu terminei de fazer a mesma solução que outros desenvolvedores, que às vezes é chamado de Padrão de design "Hierarquia paralela" ou Padrão de design "Hierarquia dupla".
(1) Você já dividiu uma única classe em uma única hierarquia de classes?
(2) Você já dividiu uma única classe em várias classes, sem hierarquia?
Se você aplicou essas soluções anteriores separadamente, elas são uma maneira de resolver alguns problemas.
Mas, e se combinarmos essas duas soluções simultaneamente?
Combine-os e você obterá esse "Padrão de Design".
Implementação
Agora, vamos aplicar o padrão de design de software "Parallel Class Hierarchy", ao seu caso.
Atualmente, você possui 2 ou mais hierarquias independentes de classes, que são muito semelhantes, têm associações ou propósitos semelhantes, têm propriedades ou métodos semelhantes.
Você deseja evitar o código ou membros duplicados ("consistência"), mas não pode mesclar essas classes diretamente em uma única, devido às diferenças entre elas.
Portanto, suas hierarquias são muito semelhantes a essa figura, mas, ainda assim, existem mais de uma:
................................................
...............+----------------+...............
...............| Common:: |...............
...............| Composite |...............
...............+----------------+...............
...............| ... |...............
...............+-------+--------+...............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
...............+-------+--------+...............
...............| Common:: |...............
...............| Viewee |...............
...............+----------------+...............
...............| ... |...............
...............+-------+--------+...............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..| Common:: |........| Common:: |..
..| Visual |........| Structural |..
..+----------------+........+----------------+..
..| ... |........| ... |..
..+----------------+........+----------------+..
................................................
Figure 1
Neste, ainda não certificado, Design Pattern, VÁRIAS HIERARQUIAS SIMILARES, SÃO FUSIONADAS EM UMA HIERARQUIA ÚNICA, e cada classe compartilhada ou comum é estendida por subclassificação.
Observe que essa solução é complexa, porque você já está lidando com várias hierarquias, portanto, é um cenário complexo.
1 A classe raiz
Em cada hierarquia, há uma classe "raiz" compartilhada.
No seu caso, existe uma classe "Composite" independente, para cada hierarquia, que pode ter algumas propriedades semelhantes e alguns métodos semelhantes.
Alguns desses membros podem ser mesclados, outros não podem ser mesclados.
Portanto, o que um desenvolvedor pode fazer é criar uma classe raiz básica e subclassificar o caso equivalente para cada hierarquia.
Na Figura 2, você pode ver um diagrama apenas para esta classe, na qual cada classe mantém seu espaço para nome.
Os membros já foram omitidos.
................................................
...............+-------+--------+...............
...............| Common:: |...............
...............| Composite |...............
...............+----------------+...............
...............| ... |...............
...............+-------+--------+...............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..| Canvas:: |........| SVG:: |..
..| Composite |........| Composite |..
..+----------------+........+----------------+..
..| ... |........| ... |..
..+----------------+........+----------------+..
................................................
Figure 2
Como você pode observar, cada classe "Composta" não está mais em uma hierarquia separada, mas mesclada em uma única hierarquia compartilhada ou comum.
Então, vamos adicionar os membros, aqueles que são iguais, podem ser movidos para a superclasse e os que são diferentes para cada classe base.
E como você já sabe, os métodos "virtual" ou "sobrecarregado" são definidos na classe base, mas substituídos nas subclasses. Como na Figura 3.
................................................
.............+--------------------+.............
.............| Common:: |.............
.............| Composite |.............
.............+--------------------+.............
.............| [+] void AddChild()|.............
.............+---------+----------+.............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..| Canvas:: |........| SVG:: |..
..| Composite |........| Composite |..
..+----------------+........+----------------+..
..| ... |........| ... |..
..+----------------+........+----------------+..
................................................
Figure 3
Observe que talvez haja algumas classes sem membros e, você pode ser tentado a remover essas classes, NÃO. Eles são chamados de "Classes ocas", "Classes enumerativas" e outros nomes.
2 As subclasses
Vamos voltar ao primeiro diagrama. Cada classe "Composite" tinha uma subclasse "Viewee", em cada hierarquia.
O processo é repetido para cada classe. Observe que na Figura 4, a classe "Common :: Viewee" desce da "Common :: Composite", mas, por simplicidade, a classe "Common :: Composite" é omitida no diagrama.
................................................
.............+--------------------+.............
.............| Common:: |.............
.............| Viewee |.............
.............+--------------------+.............
.............| ... |.............
.............+---------+----------+.............
.......................|........................
.......................^........................
....................../.\.......................
.....................+-+-+......................
.......................|........................
..........+------------+------------+...........
..........|.........................|...........
..+-------+--------+........+-------+--------+..
..| Canvas:: |........| SVG:: |..
..| Viewee |........| Viewee |..
..+----------------+........+----------------+..
..| ... |........| ... |..
..+----------------+........+----------------+..
................................................
Figure 4
Você notará que "Canvas :: Viewee" e "SVG :: Viewee" NÃO descem mais do respectivo "Composite", mas, do "Common :: Viewee" comum.
Você pode adicionar os membros agora.
......................................................
.........+------------------------------+.............
.........| Common:: |.............
.........| Viewee |.............
.........+------------------------------+.............
.........| [+] bool Validate() |.............
.........| [+] Rect GetAbsoluteBounds() |.............
.........+-------------+----------------+.............
.......................|..............................
.......................^..............................
....................../.\.............................
.....................+-+-+............................
.......................|..............................
..........+------------+----------------+.............
..........|.............................|.............
..+-------+---------+........+----------+----------+..
..| Canvas:: |........| SVG:: |..
..| Viewee |........| Viewee |..
..+-----------------+........+---------------------+..
..| |........| [+] Viewee Element |..
..+-----------------+........+---------------------+..
..| [+] void Paint()|........| [+] void addChild() |..
..+-----------------+........+---------------------+..
......................................................
Figure 5
3 Repita o processo
O processo continuará, para cada classe, "Canvas :: Visual" não descerá de "Canvas :: Viewee", comprará "Commons :: Visual", "Canvas :: Structural" não descerá de "Canvas :: Viewee ", compre em" Commons :: Structural "e assim por diante.
4 O diagrama de hierarquia 3D
Você terminará de obter uma espécie de diagrama 3D, com várias camadas, a camada superior, a hierarquia "Comum" e as camadas inferiores, cada hierarquia adicional.
Suas hierarquias de classe independentes originais, onde algo semelhante a este (Figura 6):
.................................................
..+-----------------+.......+-----------------+..
..| Common:: |.......| SVG:: |..
..| Composite |.......| Composite |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+--------+--------+..
...........|.........................|...........
...........^.........................^...........
........../.\......................./.\..........
.........+-+-+.....................+-+-+.........
...........|.........................|...........
..+--------+--------+.......+--------+--------+..
..| Common:: |.......| SVG:: |..
..| Viewee |.......| Viewee |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+--------+--------+..
...........|.........................|...........
...........^.........................^...........
........../.\......................./.\..........
.........+-+-+.....................+-+-+.........
...........|.........................|...........
..+--------+--------+.......+--------+--------+..
..| Common:: |.......| SVG:: |..
..| Visual |.......| Visual |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+--------+--------+..
...........|.........................|...........
...........^.........................^...........
........../.\......................./.\..........
.........+-+-+.....................+-+-+.........
...........|.........................|...........
..+--------+--------+.......+--------+--------+..
..| Common:: |.......| SVG:: |..
..| Rect |.......| Rect |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+-----------------+.......+-----------------+..
.................................................
Figure 6
Observe que algumas classes são omitidas e toda a hierarquia "Canvas" é omitida, por simplicidade.
A hierarquia de classes integrada final pode ser algo semelhante a este:
.................................................
..+-----------------+.../+..+-----------------+..
..| Common:: +--<.+--+ SVG:: |..
..| Composite |...\+..| Composite |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+-----------------+..
...........|.....................................
...........^.....................................
........../.\....................................
.........+-+-+...................................
...........|.....................................
..+--------+--------+.../+..+-----------------+..
..| Common:: +--<.+--+ SVG:: |..
..| Viewee |...\+..| Viewee |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+-----------------+..
...........|.....................................
...........^.....................................
........../.\....................................
.........+-+-+...................................
...........|.....................................
..+--------+--------+.../+..+-----------------+..
..| Common:: +--<.+--+ SVG:: |..
..| Visual |...\+..| Visual |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+--------+--------+.......+-----------------+..
...........|.....................................
...........^.....................................
........../.\....................................
.........+-+-+...................................
...........|.....................................
..+--------+--------+.../+..+-----------------+..
..| Common:: +--<.+--+ SVG:: |..
..| Rect |...\+..| Rect |..
..+-----------------+.......+-----------------+..
..| ... |.......| ... |..
..+-----------------+.......+-----------------+..
.................................................
Figure 7
Observe que algumas classes são omitidas e todas as classes "Canvas" são omitidas, por simplicidade, mas serão semelhantes às classes "SVG".
As classes "Comuns" podem ser representadas como uma única camada de um diagrama 3D, as classes "SVG" em outra camada e as classes "Canvas", em uma terceira camada.
Verifique se cada camada está relacionada à primeira, na qual cada classe possui uma classe pai da hierarquia "Comum".
A implementação do código pode exigir o uso de herança de interface, herança de classe ou "mixins", dependendo do que a linguagem de programação suportar.
Sumário
Como qualquer solução de programação, não se apresse na otimização, a otimização é muito importante, mas uma otimização ruim pode se tornar um problema maior do que o problema original.
Não recomendo aplicar "Solução 1" ou "Solução 2".
Na "Solução 1" não se aplica, porque a herança é necessária em cada caso.
"Solução 2", "Mixins" podem ser aplicadas, mas depois de projetar as classes e hierarquias.
Mixins, são uma alternativa para herança baseada em interface ou herança múltipla baseada em classe.
Minha proposta, Solução 3, às vezes é chamada de Padrão de Design "Hierarquia Paralela" ou Padrão de Design "Hierarquia Dupla".
Muitos desenvolvedores / designers não concordam com isso e acreditam que não deveria existir. Mas, eu usei sozinho e outros desenvolvedores como uma solução comum para problemas, como o da sua pergunta.
Outra coisa que falta. Nas soluções anteriores, o problema principal não era o de usar "mixins" ou "interfaces", mas refinar primeiro o modelo de suas classes e, posteriormente, usar um recurso de Linguagem de Programação existente.