2

さて、コードに直接ジャンプします。

public interface Visitor {

public void visitInventory(); 
public void visitMaxCount();
public void visitCountry();
public void visitSomethingElse();
public void complete();
//the idea of this visitor is that when a validator would visit it, it would validate data
//when a persister visits it, it would persist data, etc, etc.
// not sure if I making sense here...
}

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

ここに基本的な実装があります:

public class StoreValidator implements Visitor {
private List <ValidationError> storeValidationErrors = new ArrayList<ValidationError>();

public void addError(ValidationError error) {
storeValidationErrors.add(error);
}

public List<ValidationError> getErrors() {
return storeValidationErrors;
}

public void visitInventory() {
// do nothing 
}

public void visitMaxCount() {
//do nothing
}
//... etc..  all empty implementations 

}

ここで空の実装を行った理由がわかります...ここでバリデーターを作成します..これは StoreValidator を拡張します

public XYZValidator extends StoreValidator {

@Override 
public void visitInventory(Visitable visitable) { 
// do something with visitable .. cast it to expected type
// invoke a DAO, obtain results from DB
// if errors found, do addError(new ValidationError()); with msg.
}

@Override 
public void visitMaxCount(Visitable visitable) {
//do something with visitable.. 
}

// I wouldn't implement the rest coz they wouldn't make sense
// in XYZValidator.. so they are defined as empty in StoreValidator.

}

ここで、visitable は次のようになります。

public Store implements Visitable {

public void accept(Visitor visitor) {
visitor.visitInventory();
visitor.visitMaxCount();
}
}

Store オブジェクトのリストに対して次のようなコードを作成できます。

List<Store> stores; //assume this has a list of stores.
StoreValidator validator = new XYZValidator(); //or I would get it from a validatorfactory
for(Store store: stores) {
           store.accept(validator); // so even if you send a wrong validator, you are good.
}

同様に、他のメソッド (visitCountry / visitSomethinElse) の実装を提供する ABCValidator があり、StoreValidator から拡張されます。accept メソッドを定義する別のタイプのオブジェクト (ストアではない) があります。

ここで問題が発生します... たとえば、StoreValidator とは異なる FileValidator が必要です。 Visitor インターフェイスであらゆる種類のメソッドを宣言することになります。あれは正しいですか?これがあなたのやり方ですか?

パターンが間違っているのか、それとも意味があるのか​​ わかりません。あなたの考えを共有してください。

4

4 に答える 4

9

少し前に、修士論文に似たようなことを書きました。このコードは、あなたのものよりもわずかにタイプ セーフです。

interface Visitable<T extends Visitor> {

   void acceptVisitor(T visitor);
}

interface Visitor {

    /**
     * Called before any other visiting method.
     */
    void startVisit();

    /**
     * Called at the end of the visit. 
     */
    void endVisit();
}

例:

interface ConstantPoolVisitor extends Visitor {

    void visitUTF8(int index, String utf8);

    void visitClass(int index, int utf8Index);

    // ==cut==
}

class ConstantPool implements Visitable<ConstantPoolVisitor> {

    @Override
    public void acceptVisitor(ConstantPoolVisitor visitor) {
        visitor.startVisit();

        for (ConstanPoolEntry entry : entries) {
            entry.acceptVisitor(visitor);
        }

        visitor.endVisit();
    }

そうです、これは間違いなく優れた柔軟な設計であり、データが動作よりも遅く変化する場合に限ります。私の例では、データは固定された (JVM 仕様で定義された) Java バイトコードです。「動作が支配的」(バイトコードのダンプ、コンパイル、変換、リファクタリングなど) の場合、Visitor パターンを使用すると、データ クラスに触れることなく動作を変更/追加/削除できます。Visitor の別の実装を追加するだけです。

簡単にするために、Visitor インターフェースに別の visit メソッドを追加する必要があると仮定します。これにより、すべてのコードが壊れてしまいます。

別の方法として、このシナリオの戦略パターンを検討します。ストラテジー + デコレータは検証に適した設計です。

于 2009-06-03T14:03:38.553 に答える
4

与えられたコードに問題があります。あなたが与えるインターフェースには、次のようなメソッドがあります

public void visitInventory(); 

ただし、XYZValidator で次のように実装します。

public void visitInventory(Visitable visitable)

ビジター パターンは、複数のディスパッチを自動的に行わない言語 (Java など) で複数のディスパッチを実装する方法です。要件の 1 つは、関連するクラスのグループ (つまり、単一のスーパー クラスを持つサブクラスのセット) があることです。ここにはそれがないため、訪問者パターンは適切ではありません。ただし、実行しようとしているタスクは問題ありません。それはビジター パターンではありません。

Java では、次のようなコードがある場合、Visitor パターンを考える必要があります。

public void count(Item item) {
  if (item instanceof SimpleItem) {
    // do something
  } else if (item instanceof ComplexItem {
    // do something else
  } else ...
}

特に、Item のサブクラスが比較的固定されている場合。

于 2009-06-03T14:09:55.607 に答える
1

私は別の方法で訪問者パターンを使用しています..私はオブジェクトのタイプに特定の訪問者インターフェースを持っています.このインターフェースは、そのオブジェクトを訪問するためのメソッドを1つだけ宣言します..次のように:

public interface TreeNodeVisitor {
    void visit(TreeNode node);
}

オブジェクトTreeNodeはTreeNodeVisitorを受け入れることができます。これは、ノードおよび/またはその子のvisitメソッドを呼び出すだけであることを意味します。

ビジターの具体的な実装は、visitメソッドを実装し、ビジターが何をするかを示します。たとえば、ContryVisitor、InventoryVisitor などです。

このアプローチはあなたの問題を回避するはずです..

于 2009-06-03T14:04:07.583 に答える
1

おそらく、そのパターンに続くすべてが実装する単一のインターフェースにパターンを直接マップしたくないでしょう。パターンはインターフェースではなく、ソリューションを実装するための一般的な計画です。

あなたの例では、適切な状況で Visitor パターンを使用したいさまざまなビジネス オブジェクトに対して StoreVisitor インターフェイスと FileVisitor インターフェイスを作成します。

さまざまな Visitor 実装が共通のアクティビティを共有している可能性があります。そのため、これらの共通機能を定義するスーパーインターフェースを持つことができます。次に、必要に応じて特定の Visitable インターフェースまたはそのスーパークラスを使用するように、Visitable インターフェースをコーディングできます。

たとえば、FileVisitor および SQLTableVisitor インターフェイスは、DataStoreVisitor インターフェイスのサブクラスである場合があります。それで:

VisitableStore は StoreVisitor を受け入れます。

VisitableFile は Filevisitor を受け入れる、または

VisitableDataStore は DataStoreVistor (FileVisitor または SQLTableVisitor のいずれかの実装である可能性があります) を受け入れます。

  • ランダムな例を許してください。これが理にかなっていることを願っています。
于 2009-06-03T14:24:49.493 に答える