2012-03-22

Refactoring and the Visitor pattern 2. (Spring)

After showing how to decouple subtypes indirectly coupled to a base type via the Visitor pattern in the first part of this writing I demonstrate something more sophisticated.

In the same system another usage of the Visitor pattern impeded the refactoring: In the last of a chain of six singleton Spring collaborators a visitor has been used to accomplish a certain task by visiting the current instance of the blackboard subtype which has been presented in the previous post.
public class CollaboratorUsingVisitor {
  ...
  public void useVisitor(BlackBoardBaseType blackBoard) {
    ...
    TaskAccomplishingVisitor visitor = new TaskAccomplishingVisitor();
    blackBoard.accept(visitor);
    Result result = visitor.getResult();
    ...
  }
  ...
}

public class TaskAccomplishingVisitor implements BlackBoardVisitor {

  private Result result;

  public void visit(BlackBoardSubType1 blackBoardSubType) { ... }
  ...
  public void visit(BlackBoardSubTypeN blackBoardSubType) { ... }

  public Result getResult() { return result; }

}
It can be seen that on every call of the method useVisitor() a new instance of the visitor is created. Since I needed to extract the whole collaborator chain, too, I needed to abstract this visitor from the blackboard subtypes as well. But in order to achieve this a special case collaboration had to be implemented: Singleton collaborator using prototype visitor. It is not very widespread, at least according to the systems which I've seen so far, but definitely a clean solution, because Spring supports lookup-method injection since version 1.x.
There was an additional step to get in a state where the this kind of injection could be applied: Extract an interface specifying the method returning the computed result.
After finishing the refactoring this is how the Java code looked like:

public abstract class CollaboratorUsingVisitor {
  ...
  public void useVisitor(BlackBoardBaseType blackBoard) {
    ...
    TaskAccomplishingVisitor visitor = createVisitor();
    blackBoard.accept(visitor);
    Result result = visitor.getResult();
    ...
  }

  public abstract TaskAccomplishingVisitor createVisitor();
  ...
}

public class ExistingTaskAccomplishingVisitor implements BlackBoardVisitor, TaskAccomplishingVisitor {

  private Result result;

  public void visit(BlackBoardSubType1 blackBoardSubType) { ... }
  ...
  public void visit(BlackBoardSubTypeN blackBoardSubType) { ... }

  public Result getResult() { return result; }
}

public class NewTaskAccomplishingVisitor implements TaskAccomplishingVisitor {
  public Result getResult() { return new Result(); }
}
And this is a snippet of the Spring XML configuration holding the bean definitions for the old and the new module:













In case of the new visitor in the new module there were no requirements regarding what result to deliver, therefore if was completely sufficient to return just a dummy result. With this whole change the collaborator chain could be abstracted and duplicated without having to extract the blackboard subtypes.

No comments:

Post a Comment