I am currently working on a project where I need to manipulate a tree-like data structure, which I implemented with a composite pattern.
I want to be able to do different actions on this data structure, so I implemented a visitor pattern.
I was starting to see some code duplication in between my visitors, so I decided to implement a decorator pattern over my visitors. The decorators implement the Visitor interface, and their constructor accepts a Visitor as a parameter.
The idea is that the decorator executes some code before, executes its base visitor (the one passed as a parameter), and executes some code after, a bit like that:
class ConcreteDecorator {
decoratedVisitor: Visitor
visitLeaf(leaf) {
// Do some stuff before
result := decoratedVisitor.visitLeaf(leaf)
// Do some stuff after
return result;
}
visitComposite(composite) {
// Do some stuff before
result := decoratedVisitor.visitComposite(leaf)
// Do some stuff after
return result;
}
}
The decorator/visitor stuff can be setup like that:
visitor := new ConcreteVisitorA()
visitor := new ConcreteDecoratorA(visitor)
Visitor := new ConcreteDecoratorB(visitor)
The problem is that once I call visitComposite on a decorator, for example, the decorator does its stuff, then calls its child visitor's visitComposite. Since visitComposite is a recursive method, when it is called on the ConcreteVisitor and the method does its recursive calls, we lose the decorators. The decorators are only applied to the first visited node.
I thought of different solutions, like using inheritance instead of decorators, but I want to be able to reuse the decorators on different visitors, so that's not a viable option.
I also thought of using some kind of strategy pattern. The strategies would have a beforeVisitLeaf/afterVisitLeaf and beforeVisitComposite/afterVisitComposite methods. The problem is I can't implement a filter decorator, which abort the visit of certain nodes.
I managed to do a hack this solution and make it work by keeping a hook on the child visitor methods and substituting the child visitor instance methods by the decorator methods manually (I can do that because… JavaScript).
This solution is super hacky, so I was wondering if you guys had any design ideas on how to fix this.
Best Answer
From your description, I get the feeling that your code looks similar to this
If that is the case, then the root of your problem lies in the fact that you moved a responsibility of the Composite class (from the Composite pattern) into the visitComposite function of your visitor.
When calling a method of a Composite class of the Composite pattern, it is the responsibility of that class to forward the call to the child nodes as needed.
If you don't want to tie your Visitor pattern to a particular traversal order of composites and children of the Composite pattern, then you can extend your visitor classes to have two functions that should be called by the Composite class, like this
This also makes your decorator pattern on the visitors possible.