web-dev-qa-db-ja.com

訪問者の安定性とインスタンスの柔軟性

設定ファイルを生成するGUIアプリケーションに取り組んでいます。構成モデルのクラス階層があり、その階層のオブジェクトツリーをいくつかの異なるコンテキストで使用しています。現在、ビジターパターンを使用して、コンテキスト固有のコードでモデルクラスを汚染しないようにしています。

interface IConfigurationElement {
    void acceptVisitor(IConfigurationElementVisitor visitor);
}

以前のバージョンでは、ビジターの代わりにinstanceof条件のチェーンを使用していました。 2つのアプローチを比較すると、次のトレードオフが見られます。

ビジター

  • 新しいIConfigurationElementを追加する方が簡単で安全です。新しい宣言をIConfigurationElementVisitorに追加するだけで、コンパイラはすべてのビジター実装に対してエラーを生成します。 instanceofチェーンでは、新しい構成要素で拡張する必要があるすべての場所を覚えておく必要があります。基本的にinstanceofは、DRYの原則に違反します。これは、いくつかの場所でロジックが重複するためです。
  • ビジターパターンは より効率的 チェーンのinstanceofconditionsよりも

instanceof

  • instanceofの大きな利点は、その柔軟性です。たとえば、instanceofを使用すると、IConfigurationElement実装のさまざまなサブセットに対して特別なソリューションを定義でき、場合によっては同様に処理する必要があります。対照的に、訪問者は私に毎回各実装クラスのメソッドを実装することを強制します。

この種の問題に対する一般的な解決策はありますか?どういうわけかビジターを適応させることができるので、いくつかのケースに共通のソリューションを提供できますか?

8
Johannes Luong

InstanceOfでビジターを使用できます

インターフェース:

interface Visitable {
  void accept(Object visitor);
}
interface AVisitor {
  void visitA(A a);
}
interface BVisitor {
  void visitB(B b);
}
interface CVisitor {
  void visitB(C c);
}

Visitables:

class C implements Visitable {
  public void accept(Object visitor) {
    if (visitor instanceof CVisitor) {
      ((BVisitor)vistor).visitC(this);
    }
  }
}

class B implements Visitable {
  public void accept(Object visitor) {
    if (visitor instanceof BVisitor) {
      ((BVisitor)vistor).visitB(this);
    }
  }
}

class A extends B implements Visitable {
  public void accept(Object visitor) {
    super.accept(visitor);
    if (visitor instanceof AVisitor) {
      ((AVisitor)vistor).visitA(this);
    }
  }
}

訪問者:

class PrintBs implements BVisitor {
  public void visitB(B b) {
    system.out.println(b);
  }
}

class PrintAs implements AVisitor {
  public void visitA(A a) {
    system.out.println(a);
  }
}

class PrintCs implements CVisitor {
  public void visitC(C c) {
    system.out.println(c);
  }
}
class PrintAsAndCs implements CVisitor, AVisitor{
  public void visitA(A a) {
    system.out.println(a);
  }
  public void visitC(C c) {
    system.out.println(c);
  }
}

各クラスは関連するインターフェースのみを認識しているため、新しいビジターまたはビジタブルを追加するには、そのカテゴリ(ビジター/ビジター)のすべてを変更する必要があります(ビジターの場合は何も変更する必要はありません。ビジブルの場合は新しいビジターインターフェースを作成する必要がありますが、やはり既存のオブジェクトの変更)。

このように、instanceofテストのチェーンはなく、サブセットのビジターはこのサブセット外の型について知る必要さえありません。

質問は、AがBを拡張する(そしてBもVisitableである)状況で何をするかです。その場合、super.accept(visitor)をacceptに追加するだけです(したがって、instanceof-sの短いチェーンになりますが、onlu as階層が深い限り、そしてそれが問題になるほど深くてはいけませんが、手動で全体を記述する必要はありません)。

1
user470365

私はいくつかの潜在的な解決策を考えることができます:

  1. Visitor実装でプライベートメソッドを作成し、visit実装で複数のVisitorメソッドを使用して、そのプライベートメソッドを呼び出します。

  2. 上記が多くの場所で繰り返される場合、Visitorを実装し、visit実装のサブセットを共通のprotected abstractメソッドにリダイレクトする抽象クラスの作成を検討できます。

  3. 複数のVisitorインターフェースを作成します。

    interface AOrB extends IConfigurationElementVisitor {
        <Result> Result accept(AOrBVisitor<Result> visitor);
    
        interface AOrBVisitor<Result> {
            Result visit(A a);
            Result visit(B b);
        }
    }
    
    interface ThisCouldBeOfTypeB extends IConfigurationElementVisitor {
        <Result> Result accept(ThisCouldBeOfTypeBVisitor<Result> visitor);
    
        interface ThisCouldBeOfTypeBVisitor<Result> {
            Result visit(B b);
            Result visit(ThisCouldBeOfTypeB visitable);
        }
    }
    
    class A implements AOrB, ThisCouldBeOfTypeB {...}
    
    class B implements AOrB, ThisCouldBeOfTypeB {...}
    
    class C implements ThisCouldBeOfTypeB {...}
    

3はあなたが探しているものだと思います。 100%静的なポリモーフィズムであり、型が処理されない場合はコンパイラ警告が生成されます。しかし、私が考えることができる唯一の欠点は、さまざまなVisitable*インターフェイスがある場合、Visitable*実装の維持が複雑になる可能性があることです。

0
Eric

ええ、そうです。多くの構成要素に役割を割り当てることにより、それらに共通性を持たせます。あなたはinstanceofで終わるかもしれませんが、DRYの原則に違反する方法ではなく、Javaの静的型付けを回避する方法としてです。

class ConfigurationElementA implements IConfigurationElement, ICommittable {
}

class ConfigurationElementB implements IConfigurationElement, IVerifiable {
}

class Visitor {
    void accept(IConfigurationElement element) {
        if (element instanceof ICommittable) {
            // ...
        }

        // Note: not a chain of instanceofs.

        if (element instanceof IVerifiable) {
            // ...
        }
    }
}

言い換えると、訪問者に構成要素を総称的に受け入れさせ、ロールを介して実装のグループに作用させることができます。構成要素を適宜モデル化することで、必要に応じて具体的にすることができます。

ここで、Martin Fowlerの RoleInterface パターンを認識できます。

0
Mihai Danila