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.