java中的访问者模式实现 - 这看起来如何?

时间:2009-06-03 13:42:22

标签: java visitor-pattern

Alrite,我会直接跳到代码:

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.

}

现在这是一个可访问的样子:

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.
}

类似地,您将拥有ABCValidator,它将为其他方法(visitCountry / visitSomethinElse)提供实现,并且它将从StoreValidator扩展。我会有另一种类型的Object(而不是Store)定义accept方法。

我确实在这看到一个问题...... 说,我需要一个与StoreValidator不同的FileValidator,我希望它没有这些与业务相关的验证,例如visitInventory()等。但是,通过拥有一个单独的接口访问者,我最终会声明各种方法访客界面。那是对的吗?这是你怎么做的?

我不知道我的模式是否错误,或者我是否有任何意义。 请分享您的想法。

4 个答案:

答案 0 :(得分: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();
    }

所以是的,我认为,如果且仅当您的数据变化慢于您的行为时,这绝对是一个良好且灵活的设计。在我的示例中,数据是Java字节码,它是固定的(由JVM规范定义)。当“行为支配”(我想转储,编译,转换,重构等我的字节码)时,访问者模式允许您更改/添加/删除行为而不触及您的数据类。只需添加访问者的另一个实现。

为了简单起见,假设我必须在访问者界面中添加另一个访问方法:我最终会破坏所有代码。

作为替代方案,我会考虑此方案的策略模式。策略+装饰器是一种很好的验证设计。

答案 1 :(得分:4)

您的代码存在问题。您提供的界面包含

等方法
public void visitInventory(); 

但是你在XYZValidator中实现它

public void visitInventory(Visitable visitable)

visitor pattern是一种在不自动执行此操作的语言(例如Java)中实现multiple dispatch的方法。其中一个要求是您有一组相关的类(即一组具有单个超类的子类)。你没有这里,所以访客模式不合适。但是,您尝试执行的任务很好,它不是访问者模式。

在Java中,如果您有像

这样的代码,则应该考虑访问者模式
public void count(Item item) {
  if (item instanceof SimpleItem) {
    // do something
  } else if (item instanceof ComplexItem {
    // do something else
  } else ...
}

特别是如果Item的子类相对固定。

答案 2 :(得分:1)

我正在以不同的方式使用访问者模式。我有一个特定的访问者接口用于某种类型的对象,这个接口只声明一个方法 - 用于访问该对象..像这样:

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

对象 TreeNode 可以接受 TreeNodeVisitor ,这意味着他只是为节点和/或它的子节点调用它的访问方法。< / p>

访问者的具体实现实现了访问方法,并说明访问者将做什么......例如 ContryVisitor,InventoryVisitor等

这种方法应该避免你的问题......

答案 3 :(得分:1)

您可能不希望将模式直接映射到单个接口,该模式后面的所有内容都会实现。模式不是接口,它们是实现解决方案的一般计划。

在您的示例中,您将为希望在适当情况下使用访问者模式的不同业务对象创建StoreVisitor接口和FileVisitor接口。

可能不同的Visitor实现共享公共活动 - 因此您可以使用定义这些常用功能的超接口。然后,您可以将可访问接口编码为使用特定的Visitable接口或适当的超类。

例如,FileVisitor和SQLTableVisitor接口可能是DataStoreVisitor接口的子类。然后:

VisitableStore接受StoreVisitor,

VisitableFile接受Filevisitor或

VisitableDataStore接受DataStoreVistor(可能是FileVisitor或SQLTableVisitor的实现)。

  • 原谅随机的例子,我希望这是有道理的。