Estoy trabajando en una aplicación GUI que genera un archivo de configuración. Tengo una jerarquía de clases para el modelo de configuración y uso un árbol de objetos de esa jerarquía en varios contextos diferentes. Actualmente, uso el patrón Visitor para evitar contaminar mis clases de modelo con código específico de contexto.
interface IConfigurationElement {
void acceptVisitor(IConfigurationElementVisitor visitor);
}
En una versión anterior, utilizaba cadenas de instanceof
condiciones en lugar de Visitor. Comparando los dos enfoques, veo los siguientes intercambios.
Visitante
- Es más fácil y seguro agregar nuevos
IConfigurationElement
. Simplemente agregue una nueva declaraciónIConfigurationElementVisitor
y el compilador genera errores para todas las implementaciones de los visitantes. Con lasinstanceof
cadenas, debe recordar todos los lugares que debe extender con el nuevo elemento de configuración. Básicamenteinstanceof
viola el principio DRY ya que duplica la lógica en varios lugares. - El patrón de visitante es más eficiente que una cadena de
instanceof
condiciones.
en vez de
- La gran ventaja de
instanceof
es su flexibilidad. Por ejemplo,instanceof
me permite definir soluciones especiales para diferentes subconjuntos deIConfigurationElement
implementaciones que deben manejarse de manera similar en algunos casos. Por el contrario, Visitor me obliga a implementar un método para cada clase de implementación cada vez.
¿Existe una solución común para este tipo de problema? ¿Puedo adaptar el visitante de alguna manera, para poder proporcionar una solución común para algunos casos?
java
design-patterns
object-oriented-design
Johannes Luong
fuente
fuente
Respuestas:
Puede usar el visitante con la instancia
interfaces:
Visitables:
Visitantes:
cada clase solo conoce sus interfaces relacionadas, por lo que agregar nuevos visitantes o visitantes requiere cambiar todo en esa categoría (visitante / visitable) (para el visitante, no requiere cambiar nada, para visitar requiere crear una nueva interfaz de visitante, pero nuevamente, no cambio de objetos existentes).
De esta manera, no hay una cadena de instancias de pruebas y el visitante para el subconjunto ni siquiera necesita saber acerca de los tipos fuera de este subconjunto.
La pregunta es qué hacer con la situación en la que A extiende a B (y B también es Visitable), en ese caso, podría agregar super.accept (visitante) en accept (por lo tanto, sería una cadena corta de instancias de s, pero solo como siempre y cuando la jerarquía sea profunda, y no debería ser demasiado profunda para importar, y no es necesario que la escriba por completo de forma manual).
fuente
IConfigurationElementVisitor
y simplemente verificar los tipos de visitantes específicosVisitable
. Si bien esta es una posible solución, veo algunos inconvenientes. Primero, esto eliminaría la estabilidad de Visitor de la que hablé y segundo, incorporaría el conocimiento sobre los visitantes en misVisitable
implementaciones, lo que evitar era parte de la razón para usar Visitor en primer lugar.Bueno, si puedes. Capture los puntos en común de los muchos elementos de configuración asignándoles roles. Puede terminar con instanceof, pero no de una manera que viole el principio DRY, sino más bien como una forma de evitar la escritura estática de Java.
En otras palabras, permite que el visitante acepte elementos de configuración genéricamente y actúe en grupos de implementaciones a través de roles. Tenga en cuenta que puede ir tan específico como necesite modelando sus elementos de configuración en consecuencia.
Puede reconocer aquí el patrón RoleInterface de Martin Fowler .
fuente
IConfigurationElement
cuales se pueden manejar específicamente en función de la membresía establecida. Desafortunadamente, tan pronto como distingas los objetos coninstanceof
tu compilador no podrás ayudarte si olvidas actualizar a uno de tus visitantes. Su solución tiene el beneficio de que todos losinstanceof
operadores están integrados en un tipo común, lo que ayuda a encontrarlos buscando el tipo.accept
firmas de métodos. Para esto, sugerí un enfoque general, uno que puede adaptar para satisfacer sus necesidades, más o menos específico en torno a suIConfigurationElement
implementación. En segundo lugar, querías la flexibilidad de la instancia de, presumiblemente (en el primer caso) porque tienes rasgos comunes en tus clases de implementación; de lo contrario, no hay razón para evitar laaccept
proliferación para empezar. Ahí es donde sugeríRoleInterface
cuál usa de manerainstanceof
diferente.instanceof
:IConfigurationElement
ImplementeIRoleEnabled
y haga que el visitante visite cada elemento de configuración como un elemento habilitado para el rolvisitRoleEnabled
, y quevisitRoleEnabled
en cada clase de implementación vuelva a llamar al visitante para cada uno de los roles implementados. Pero con esto comenzamos a ir salvajeinstanceof
cuando realmente está bien.)Se me ocurren algunas soluciones potenciales:
cree un método privado en la
Visitor
implementación y tenga variosvisit
métodos en laVisitor
implementación, llame a ese método privado.Si lo anterior se repite en muchos lugares, puede considerar crear una clase abstracta que implemente
Visitor
y redirija un subconjunto devisit
implementaciones a unprotected abstract
método común .crear múltiples
Visitor
interfaces:Creo que 3 es lo que estás buscando. es 100% polimorfismo estático y genera advertencias del compilador si no se maneja un tipo. Pero la única desventaja que puedo pensar en 3 es que mantener las
Visitable*
implementaciones podría volverse complejo si hay muchasVisitable*
interfaces diferentes .fuente