views:

1257

answers:

4

Alrite, I am gonna jump straight to the code:

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

here is a base implementation:

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 

}

You will see why I did an empty implementation here... I would write a validator now.. which extends 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.

}

Now here is what a visitable would look like:

public Store implements Visitable {

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

I could have code that does something like this on a list of Store objects:

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

Similarly you would have ABCValidator which would provide implementation for other methods (visitCountry / visitSomethinElse) and it would extend from StoreValidator. I would have another type of Object (not Store) defining accept method.

I do see a problem here... Say, I need a FileValidator which is different from StoreValidator, I would expect it to have none of these business related validations such as visitInventory(), etc. But, by having a single interface Visitor, I would endup declaring all kinds of methods in Visitor interface. Is that correct? Is this how you do it?

I don't know if I got the pattern wrong, or if I am making any sense. Please share your thoughts.

+4  A: 

Some time ago I writed something similar for my master thesis. This code is slightly type safe than your:

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

example:

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

so yes, I think that this definitely a good and flexible design if, and only if, your data changes slower than your behaviour. In my example the data is Java bytecode, that is fixed (defined by the JVM specification). When "behaviour dominates" (I want to dump, compile, transform, refactor, etc my bytecode) the Visitor pattern let you to change/add/remove behaviour without touching your data classes. Just add another implementation of Visitor.

For the sake of simplicity assume that I must add another visit method to my Visitor interface: I would end in breaking all my code.

As alternative I would consider the strategy pattern for this scenario. Strategy + decorator is a good design for validation.

dfa
@dfa strategy does makes sense, however, this would result in a as many strategies as many business rules. I would then have that many classes - which would be too many. The idea behind using a visitor here is that if I were to add behavior to my code, I shouldn't be doing much.
Jay
"... [if] your data changes slower than your behaviour." - that's one of the best descriptions I've heard in regards to the visitor pattern. +1
Grundlefleck
+1  A: 

Hi,

I'm using a visitor pattern in a different way.. I have a specific Visitor interface for a type of object and this interface declares only one method - for visiting that object.. like this:

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

the object TreeNode can accept TreeNodeVisitors which means he just calls it's visit method for the node and/or it's children..

The concrete implementation of the visitor implements the visit method and says what the visitor will do.. for example ContryVisitor, InventoryVisitor, etc

This approach should avoid your probleam..

Martin Lazar
This would mean, I would have as many Visitor classes as many as my business rules. It would result in a class explosion.
Jay
+1  A: 

There is a problem with your code as given. The interface you give has methods such as

public void visitInventory();

but you then implement it in XYZValidator as

public void visitInventory(Visitable visitable)

The visitor pattern is a way to implement multiple dispatch in languages that do not do that automatically (such as Java). One of the requirements is that you have a group of related classes (i.e. a set of subclasses with a single super class). You don't have that here, so the visitor pattern is not appropriate. The task you are trying to do, however, is fine, it is just not the Visitor pattern.

In Java, you should think of the Visitor pattern if you have code like

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

particulary if the subclasses of Item are relatively fixed.

Kathy Van Stone
+1  A: 

You probably don't want to map a pattern directly to a single interface that everything following that pattern implements. Patterns are NOT Interfaces, they are general plans for implementing a solution.

In your example you would create a StoreVisitor interface and a FileVisitor interface for the different business objects that wish to use the Visitor pattern in the appropriate circumstances.

It might be that different Visitor implementations share common activities - so you could have a superinterface that defines those common functions. You could then code Visitable interfaces to use either the specific Visitable interface or it's superclass as appropriate.

For example, the FileVisitor and SQLTableVisitor interfaces might be a subclass of a DataStoreVisitor interface. Then:

VisitableStore accepts a StoreVisitor,

VisitableFile accepts a Filevisitor, or

VisitableDataStore accepts a DataStoreVistor (which might be an implementation of either FileVisitor or SQLTableVisitor).

  • forgive the random examples, I hope this makes sense.
AndyT