5

私はビジター パターンに少し慣れていませんが、ビジターの実装が必要なタスクがあります ("instanceof" チェックを回避したい場合)。

ラベル、パネル、ウィジェット(チェックボックス、リストボックス、テキストボックスなど)のいくつかのgwt要素のラッパーであるクラスがあります。UI の類似部分のコレクションとして配列を使用します。例: ラベル + チェックボックス、ラベル + テキスト ボックス。ラベル+ボタン等

一部の要素は異なる方法で構築されます (たとえば、Panel から派生した別のクラスの一部)。その結果、同じコンストラクターが 2 つありますが、オーバーロードされたメソッドが 1 か所で使用されています。これらのコンストラクターをマージし、前述のメソッド内で「instanceof」を使用して要素をチェックできます。しかし、私はこの解決策が嫌いで、訪問者パターンを使用して置き換えたいと考えています。実を言うと、私はそれを行う方法がわかりません。あなたの助けを願っています。

これが私が持っているものの例です:

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);
    }
}

このようなもの(私はそれを最大限に抽象化しようとしましたが、実際にはもっと難しいです)。

したがって、「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);
  }
}

「instanceof」から保存され、コンストラクターを 1 つだけ残し、可能であれば、オーバーロードされたバージョンのない init メソッドを 1 つ残したいと考えています。ご提案、ご協力ありがとうございます。

PS> 繰り返しますが、上記のクラスは偽造されており、特にこの String ラベルでは誤解が生じているようです :)

4

2 に答える 2

3

2 つのコンストラクターを使用した既存のソリューションである IMO は問題ありません。

戦略パターンを使用して、コンストラクターPanelProviderに Object の代わりにインターフェイスのインスタンスを取得させることができます。このインターフェイスには、次のメソッドがあります。

Panel createPanel(Widget widget);

. クライアントは、 のインスタンスStringPanelProviderまたは のインスタンスをConstructedClassPanelProviderコンストラクターに渡します。したがって、コンストラクターは次のようになります。

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

StringPanelProvider の実装は次のようになります。

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);
    }
}

ConstructedClassPanelProvider は同じように見えます。

Visitor パターンを本当に使用したい場合は、上記を少し変更する必要があります。

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);
       }
   });
}

しかし、これは私にはオーバーエンジニアリングのように見えます。

于 2012-05-12T12:49:57.133 に答える
2

訪問者パターンを使用した 1 つの実装は次のとおりです。

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);
    }
}

このソリューションは、JB Nizet のものと非常によく似ています。ConstructorArgVisitorこの実装のインターフェースと JB Nizet のインターフェースの違いVisitorは、メソッド名です。visitメソッドは ではオーバーロードされていますConstructorArgVisitorが、JB Nizet のVisitorではメソッド名に型が含まれています (例: stringVisited)。メソッドのオーバーロードは、Wikipedia ページvisitの訪問者パターンの例によく似ています。

JB Nizet の意見では、ビジター パターンを使用するのは少しやり過ぎかもしれません。ただし、PanelProviderJB Nizet が推奨するように a を使用する場合、引数が aStringまたはConstructedClass事前にわかっていない限りinstanceof、回避しようとしているチェックを行う必要がある場合があります。

これは私の個人的な好みなので、無視してもかまいません。Misko Hevery が " Flaw: Constructor Does Real Work " で推奨しているように、コンストラクターで作業を行わないようにしてください。たとえば、構築ロジックをファクトリに移動できます。以下は、上記の訪問者パターンの修正版を使用しています。

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);
}

また、構築中に静的メソッドを呼び出していることもわかります。多くのコードベースでは GUI のテストが難しいことで有名ですが、「欠陥: 脆弱なグローバル状態とシングルトン」と「ガイド: テスト可能なコードの記述」を読むことをお勧めします。

于 2012-05-12T14:14:22.053 に答える