Design – Combining composite, decorator and visitor patterns

decoratordesignvisitor-pattern

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.

enter image description here

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

class Composite 
{
    accept<T>(Visitor<T> visitor) {
        visitor.visitComposite(this)
    }
}

class ConcreteVisitor // inherits Visitor<T>
{
    visitComposite(composite) {
        // do some work
        for child in composite.children {
            child.accept(this)
        }
        // do some more work
    }
}

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

class Composite 
{
    accept<T>(Visitor<T> visitor) {
        visitor.visitCompositePreChildren(this)
        for child in children {
            child.accept(visitor)
        }
        visitor.visitCompositePostChildren(this)
    }
}

class ConcreteVisitor // inherits Visitor<T>
{
    visitCompositePreChildren(composite) {
        // do some work before the child nodes
    }
    visitCompositePostChildren(composite) {
        // do some work after the child nodes
    }
}

This also makes your decorator pattern on the visitors possible.