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 instanceofcondiciones 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ónIConfigurationElementVisitory el compilador genera errores para todas las implementaciones de los visitantes. Con lasinstanceofcadenas, debe recordar todos los lugares que debe extender con el nuevo elemento de configuración. Básicamenteinstanceofviola 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
instanceofcondiciones.
en vez de
- La gran ventaja de
instanceofes su flexibilidad. Por ejemplo,instanceofme permite definir soluciones especiales para diferentes subconjuntos deIConfigurationElementimplementaciones 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
IConfigurationElementVisitory 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 misVisitableimplementaciones, 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
IConfigurationElementcuales se pueden manejar específicamente en función de la membresía establecida. Desafortunadamente, tan pronto como distingas los objetos coninstanceoftu compilador no podrás ayudarte si olvidas actualizar a uno de tus visitantes. Su solución tiene el beneficio de que todos losinstanceofoperadores están integrados en un tipo común, lo que ayuda a encontrarlos buscando el tipo.acceptfirmas 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 suIConfigurationElementimplementació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 laacceptproliferación para empezar. Ahí es donde sugeríRoleInterfacecuál usa de manerainstanceofdiferente.instanceof:IConfigurationElementImplementeIRoleEnabledy haga que el visitante visite cada elemento de configuración como un elemento habilitado para el rolvisitRoleEnabled, y quevisitRoleEnableden cada clase de implementación vuelva a llamar al visitante para cada uno de los roles implementados. Pero con esto comenzamos a ir salvajeinstanceofcuando realmente está bien.)Se me ocurren algunas soluciones potenciales:
cree un método privado en la
Visitorimplementación y tenga variosvisitmétodos en laVisitorimplementación, llame a ese método privado.Si lo anterior se repite en muchos lugares, puede considerar crear una clase abstracta que implemente
Visitory redirija un subconjunto devisitimplementaciones a unprotected abstractmétodo común .crear múltiples
Visitorinterfaces: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