2012-05-12 11 views
5

Soy un poco antipático con el patrón Visitor, pero tengo una tarea que debe tener la implementación Visitor (si quiero evitar las verificaciones "instanceof").Cómo utilizar el patrón de visitante para sustituir "instanceof"

Tengo una clase que es un contenedor para varios elementos gwt: Etiqueta, Panel, Widget (puede ser casilla de verificación, listbox, cuadro de texto, etc.). Uso una matriz como una colección de partes similares de UI. P.ej. Etiqueta + casilla de verificación, Etiqueta + cuadro de texto; Etiqueta + botón, etc.

Algunos elementos se construyen de forma diferente (parte de otra clase derivada, por ejemplo, del Panel). Entonces, como resultado, tengo dos constructores que son iguales, pero en un lugar se usa el método sobrecargado. Puedo fusionar estos constructores y verificar el elemento usando "instanceof" dentro del método mencionado. Pero no me gusta esta solución y quiero sustituirla por el patrón Visitor. A decir verdad, no sé cómo hacerlo y espero que lo ayudes.

Aquí es un ejemplo de lo que tengo:

public class MyWidgets { 
    private String stringLabel; 
    private Widget widget; 
    private Panel panel; 

    public MyWidgets(String stringLabel, Widget widget) { 
     this.stringLabel = stringLabel; 
     this.widget = widget; 

     initPanel(stringLabel, widget); 
    } 

    public MyWidgets(ConstructedClass cs, Widget widget) { 
     this.widget = widget; 

     initPanel(cs, widget); 
    } 

    private initPanel(String label, Widget widget) { 
     panel = SomeStaticUtilityClass.initPanel(new Label(label), widget); 
    } 

    private initPanel(ConstructedClass cs, Widget widget) { 
     panel = SomeStaticUtilityClass(cs, widget); 
    } 
} 

Algo como esto (he tratado de hacerlo máxima abstracto, en realidad es más difícil).

Así que tengo una solución utilizando "instanceof":

private initPanel(Object object, Widget widget) { 
    if(object instanceof String) { 
    panel = SomeStaticUtilityClass.initPanel(new Label(label), widget); 
    } 
    if(object instanceof ConstructedClass) { 
    panel = SomeStaticUtilityClass.initPanelFromObject(cs, widget); 
    } 
} 

quiero ser salvado de "instanceof" y dejar sólo un constructor e incluso, si es posible, un método de inicialización sin que su versión sobrecargada . Gracias de antemano por sus sugerencias, ayuda.

P.S> repito, que la clase anterior se fabrica, y se parece a un malentendido sobre todo con esta etiqueta Cadena :)

+0

Hmm ... algo parece extraño con la siguiente línea: "panel = SomeStaticUtilityClass (cs, widget);". ¿SomeStaticUtilityClass es una clase o un método? :) – Javaguru

+0

Corregí el ejemplo – Dragon

Respuesta

3

OMI, su solución existente, con dos constructores, está muy bien.

Puede utilizar el patrón de estrategia y hacer que su constructor tome una instancia de alguna interfaz PanelProvider en lugar de Object. Esta interfaz tendría el siguiente método:

Panel createPanel(Widget widget); 

. Los clientes pasarían una instancia de StringPanelProvider o una instancia de ConstructedClassPanelProvider al constructor. Su constructor sería por lo tanto verse como:

public MyWidgets(PanelProvider panelProvider, Widget widget) { 
    this.widget = widget; 
    this.panel = panelProvider.createPanel(widget); 
} 

Y la aplicación StringPanelProvider se vería

public class StringPanelProvider implements PanelProvider { 

    private String s; 

    public StringPanelProvider(String s) { 
     this.s = s; 
    } 

    @Override 
    public Panel createPanel(Widget widget) { 
     return SomeStaticUtilityClass.initPanel(new Label(s), widget); 
    } 
} 

El ConstructedClassPanelProvider sería la misma.

Si realmente desea utilizar el patrón del visitante entonces tendrá que modificar lo anterior un poco:

public interface Visitable { 
    void accept(Visitor visitor); 
} 

public interface Visitor { 
    void stringVisited(String s); 
    void constructedClassVisited(ConstructedClass cs); 
} 

public class StringVisitable { 
    private String s; 

    public StringVisitable(String s) { 
     this.s = s; 
    } 

    void accept(Visitor visitor) { 
     visitor.stringVisited(s); 
    } 
} 

// similar for ConstructedClassVisitable 

public MyWidgets(Visitable visitable, final Widget widget) { 
    this.widget = widget; 
    visitable.accept(new Visitor() { 
     public void stringVisited(String s) { 
      panel = SomeStaticUtilityClass.initPanel(new Label(label), widget); 
     } 

     public void constructedClassVisited(ConstructedClass cs) { 
      panel = SomeStaticUtilityClass.initPanelFromObject(cs, widget); 
     } 
    }); 
} 

Pero esto se parece a mí manipulación excesiva.

+0

Creo que el patrón de estrategia junto con una fábrica de paneles parece ser más apropiado que usar un visitante. – Javaguru

+0

Ambas variantes: la suya y la de creemama son buenas. Fueron útiles para mí. Muchas gracias. – Dragon

2

Una aplicación utilizando el visitor pattern es el siguiente:

public interface ConstructionArgVisitor { 
    void visit(LabelText text); 

    void visit(ConstructedClass clazz); 
} 

public interface ConstructionArg { 
    void accept(ConstructionArgVisitor visitor); 
} 

public class LabelText implements ConstructionArg { 
    private final String text; 

    public LabelText(String str) { 
     this.text = str; 
    } 

    @Override 
    public void accept(ConstructionArgVisitor visitor) { 
     visitor.visit(this); 
    } 

    public String getString() { 
     return this.text; 
    } 
} 

public class ConstructedClass implements ConstructionArg { 
    @Override 
    public void accept(ConstructionArgVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public class MyWidgets implements ConstructionArgVisitor { 
    private String stringLabel; 
    private Widget widget; 
    private Panel panel; 

    public MyWidgets(ConstructionArg constructionArg, Widget widget) { 
     this.widget = widget; 
     constructionArg.accept(this); 
    } 

    @Override 
    public void visit(LabelText labelText) { 
     this.stringLabel = labelText.getString(); 
     this.panel = SomeStaticUtilityClass.initPanel(new Label(labelText.getString()), this.widget); 
    } 

    @Override 
    public void visit(ConstructedClass clazz) { 
     this.panel = SomeStaticUtilityClass.initPanelFromObject(clazz, this.widget); 
    } 
} 

Esta solución es muy similar a uno de JB Nizet.La diferencia entre la implementación ConstructorArgVisitor y la interfaz Visitor de JB Nizet son los nombres de los métodos. El método visit está sobrecargado en ConstructorArgVisitor, mientras que en JB Nizet's Visitor, los nombres de método contienen el tipo en ellos (por ejemplo, stringVisited). La sobrecarga del método visit se asemeja más al ejemplo del visitor pattern on the Wikipedia page.

Estoy de acuerdo con JB Nizet en que el uso del patrón de visitante puede ser un poco sobredimensionado; sin embargo, si usa un PanelProvider como recomienda JB Nizet, a menos que sepa que el argumento es un String o ConstructedClass con anticipación, es posible que deba hacer un cheque instanceof, el cual está tratando de evitar.

Ahora esta es mi preferencia personal, por lo que puede descartar si lo desea: Trate de no hacer el trabajo en el constructor como lo recomienda Misko Hevery en "Flaw: Constructor does Real Work". Por ejemplo, puede mover la lógica de construcción a una fábrica. A continuación se utiliza una versión modificada del patrón visitante arriba:

public interface ConstructionArgVisitor<T> { 
    T visit(LabelText text); 

    T visit(ConstructedClass clazz); 
} 

public interface ConstructionArg { 
    <T> T accept(ConstructionArgVisitor<T> visitor); 
} 

public class LabelText implements ConstructionArg { 
    private final String text; 

    public LabelText(String str) { 
     this.text = str; 
    } 

    @Override 
    public <T> T accept(ConstructionArgVisitor<T> visitor) { 
     return visitor.visit(this); 
    } 

    public String getString() { 
     return this.text; 
    } 
} 

public class ConstructedClass implements ConstructionArg { 
    @Override 
    public <T> T accept(ConstructionArgVisitor<T> visitor) { 
     return visitor.visit(this); 
    } 
} 

public class MyWidgetsFactory implements ConstructionArgVisitor<MyWidgets> { 
    private final Widget widget; 

    public MyWidgetsFactory(Widget widget) { 
     this.widget = widget; 
    } 

    public MyWidgets createMyWidgets(ConstructionArg constructionArg) { 
     return constructionArg.accept(this); 
    } 

    @Override 
    public MyWidgets visit(LabelText text) { 
     return new MyWidgets(text.getString(), this.widget, SomeStaticUtilityClass.initPanel(
       new Label(text.getString()), this.widget)); 
    } 

    @Override 
    public MyWidgets visit(ConstructedClass clazz) { 
     return new MyWidgets(null, this.widget, SomeStaticUtilityClass.initPanelFromObject(clazz, this.widget)); 
    } 
} 

public class MyWidgets { 
    private final String stringLabel; 
    private final Widget widget; 
    private final Panel panel; 

    public MyWidgets(String stringLabel, Widget widget, Panel panel) { 
     this.stringLabel = stringLabel; 
     this.widget = widget; 
     this.panel = panel; 
    } 
} 

public static void main(String[] args) { 
    final Widget widget = ...; 
    final MyWidgetsFactory factory = new MyWidgetsFactory(widget); 

    // create MyWidgets from label text 
    final String str = ...; 
    final MyWidgets labelWidget = factory.createMyWidgets(new LabelText(str)); 

    // create MyWidgets from constructed class 
    final ConstructedClass clazz = ...; 
    final MyWidgets constructedClassWidget = factory.createMyWidgets(clazz); 
} 

También veo que usted está llamando a un método estático durante la construcción. Aunque en muchas bases de datos, las GUI son muy difíciles de probar, le recomendamos que lea "Flaw: Brittle Global State & Singletons" y "Guide: Writing Testable Code".

Cuestiones relacionadas