Estou trabalhando em um aplicativo GUI que gera um arquivo de configuração. Eu tenho uma hierarquia de classes para o modelo de configuração e uso uma árvore de objetos dessa hierarquia em vários contextos diferentes. Atualmente, eu uso o padrão Visitor para evitar poluir minhas classes de modelo com código específico de contexto.
interface IConfigurationElement {
void acceptVisitor(IConfigurationElementVisitor visitor);
}
Em uma versão anterior, usei cadeias de instanceof
condições em vez do Visitor. Comparando as duas abordagens, vejo as seguintes trocas.
Visitante
- É mais fácil e seguro adicionar novos
IConfigurationElement
. Basta adicionar uma nova declaraçãoIConfigurationElementVisitor
e o compilador gera erros para todas as implementações de visitantes. Cominstanceof
cadeias, você precisa lembrar de todos os lugares que precisa estender com o novo elemento de configuração. Basicamente,instanceof
viola o princípio DRY, pois duplica a lógica em vários lugares. - O padrão de visitantes é mais eficiente que uma cadeia de
instanceof
condições
instancia de
- A grande vantagem
instanceof
é a sua flexibilidade. Por exemplo,instanceof
permite-me definir soluções especiais para diferentes subconjuntos deIConfigurationElement
implementações que precisam ser tratadas de maneira semelhante em alguns casos. Por outro lado, o Visitor me obriga a implementar um método para cada classe de implementação todas as vezes.
Existe uma solução comum para esse tipo de problema? Posso adaptar o Visitante de alguma forma, para fornecer uma solução comum para alguns casos?
java
design-patterns
object-oriented-design
Johannes Luong
fonte
fonte
Respostas:
Você poderia usar o visitante com instanceOf
interfaces:
Visíveis:
Visitantes:
cada classe conhece apenas suas interfaces relacionadas; portanto, adicionar novos visitantes ou visitáveis exige a alteração de tudo nessa categoria (visitante / visitável) (para visitante, não requer alteração de nada, para visitável requer a criação de nova interface de visitante, mas, novamente, não mudança de objetos existentes).
Dessa forma, não há uma cadeia de instâncias de testes e o visitante do subconjunto nem precisa saber sobre tipos fora desse subconjunto.
A questão é o que fazer com a situação em que A estende B (e B também é Visível); nesse caso, você pode simplesmente adicionar super.accept (visitante) em accept (portanto, seria uma cadeia curta de instâncias de s, mas apenas como desde que a hierarquia seja profunda e não deva ser muito profunda para importar, e você não precisa escrevê-la inteira manualmente).
fonte
IConfigurationElementVisitor
e apenas verificar tipos específicos de visitantesVisitable
. Embora esta seja uma solução possível, vejo algumas desvantagens. Primeiro, isso removeria a estabilidade do Visitor de que falei e, em segundo lugar, atrairia o conhecimento sobre visitantes para minhasVisitable
implementações, o que evitar era parte do motivo para usar o Visitor em primeiro lugar.Bem, sim, você poderia. Capture os pontos em comum dos muitos elementos de configuração atribuindo funções a eles. Você pode acabar com instanceof, mas não de uma maneira que viole o princípio DRY, mas como uma maneira de contornar a digitação estática do Java.
Em outras palavras, você permite que o visitante aceite elementos de configuração genericamente e atue em grupos de implementações por meio de funções. Observe que você pode ser o mais específico possível modelando seus elementos de configuração de acordo.
Você pode reconhecer aqui o padrão RoleInterface de Martin Fowler .
fonte
IConfigurationElement
quais podem ser manipulados especificamente com base na associação ao conjunto. Infelizmente, assim que você distinguir objetos cominstanceof
seu compilador, não será possível ajudá-lo se você esquecer de atualizar um de seus visitantes. Sua solução tem o benefício de que todos osinstanceof
operadores são incorporados em um tipo comum, o que ajuda a encontrá-los procurando pelo tipo.accept
assinaturas de método. Para isso, sugeri uma abordagem abrangente, que você pode adaptar para atender às suas necessidades, tornando-se mais ou menos específica em suaIConfigurationElement
implementação. Segundo, você desejava a flexibilidade da instância de, presumivelmente (no primeiro caso), porque você tem características comuns em suas classes de implementação - caso contrário, não há razão para evitar aaccept
proliferação no início. Foi aí que sugeriRoleInterface
que usa de formainstanceof
diferente.instanceof
:IConfigurationElement
ImplementarIRoleEnabled
e fazer com que o visitante visite cada elemento de configuração como um item de função habilitadovisitRoleEnabled
, e tervisitRoleEnabled
em cada classe de implementação uma chamada de volta ao visitante para cada uma das funções implementadas. Mas com isso, estamos começando a avançar selvagem eminstanceof
quando ele está realmente fazendo muito bem).Eu posso pensar em algumas soluções em potencial:
crie um método privado na
Visitor
implementação e tenha váriosvisit
métodos naVisitor
implementação que chamam esse método privado.se o acima for repetido em muitos lugares, considere a criação de uma classe abstrata que implemente
Visitor
e redirecione um subconjunto devisit
implementações para umprotected abstract
método comum .crie várias
Visitor
interfaces:Eu acho que 3 é o que você está procurando. é 100% polimorfismo estático e gera avisos do compilador se um tipo não for tratado. Mas a única desvantagem que consigo pensar em 3 é que manter as
Visitable*
implementações pode se tornar complexa se houver muitasVisitable*
interfaces diferentes .fonte