Skip to main content

Acyclic Visitor

BehavioralDecouplingExtensibilityAbout 2 min

Intent

The Acyclic Visitor pattern decouples operations from an object hierarchy, allowing you to add new operations without modifying the object structure directly.

Explanation

Real world example

We have a hierarchy of modem classes. The modems in this hierarchy need to be visited by an external algorithm based on filtering criteria (is it Unix or DOS compatible modem).

In plain words

Acyclic Visitor allows functions to be added to existing class hierarchies without modifying the hierarchies.

WikiWikiWebopen in new window says

The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating the dependency cycles that are inherent to the GangOfFour VisitorPattern.

Programmatic Example

Here's the Modem hierarchy.

public abstract class Modem {
    public abstract void accept(ModemVisitor modemVisitor);
}

public class Zoom extends Modem {
  ...

    @Override
    public void accept(ModemVisitor modemVisitor) {
        if (modemVisitor instanceof ZoomVisitor) {
            ((ZoomVisitor) modemVisitor).visit(this);
        } else {
            LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
        }
    }
}

public class Hayes extends Modem {
  ...

    @Override
    public void accept(ModemVisitor modemVisitor) {
        if (modemVisitor instanceof HayesVisitor) {
            ((HayesVisitor) modemVisitor).visit(this);
        } else {
            LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
        }
    }
}

Next we introduce the ModemVisitor hierarchy.

public interface ModemVisitor {
}

public interface HayesVisitor extends ModemVisitor {
    void visit(Hayes hayes);
}

public interface ZoomVisitor extends ModemVisitor {
    void visit(Zoom zoom);
}

public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
}

public class ConfigureForDosVisitor implements AllModemVisitor {
  ...

    @Override
    public void visit(Hayes hayes) {
        LOGGER.info(hayes + " used with Dos configurator.");
    }

    @Override
    public void visit(Zoom zoom) {
        LOGGER.info(zoom + " used with Dos configurator.");
    }
}

public class ConfigureForUnixVisitor implements ZoomVisitor {
  ...

    @Override
    public void visit(Zoom zoom) {
        LOGGER.info(zoom + " used with Unix configurator.");
    }
}

Finally, here are the visitors in action.

    var conUnix=new ConfigureForUnixVisitor();
        var conDos=new ConfigureForDosVisitor();
        var zoom=new Zoom();
        var hayes=new Hayes();
        hayes.accept(conDos);
        zoom.accept(conDos);
        hayes.accept(conUnix);
        zoom.accept(conUnix);   

Program output:

    // Hayes modem used with Dos configurator.
    // Zoom modem used with Dos configurator.
    // Only HayesVisitor is allowed to visit Hayes modem
    // Zoom modem used with Unix configurator.

Class diagram

alt text
Acyclic Visitor

Applicability

This pattern can be used:

  • When you need to add a new function to an existing hierarchy without the need to alter or affect that hierarchy.
  • When there are functions that operate upon a hierarchy, but which do not belong in the hierarchy itself. e.g. the ConfigureForDOS / ConfigureForUnix / ConfigureForX issue.
  • When you need to perform very different operations on an object depending upon its type.
  • When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
  • When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.

Tutorials

Consequences

Benefits:

  • No dependency cycles between class hierarchies.
  • No need to recompile all the visitors if a new one is added.
  • Does not cause compilation failure in existing visitors if class hierarchy has a new member.

Trade-offs:

  • Violates Liskov's Substitution Principleopen in new window by showing that it can accept all visitors but actually only being interested in particular visitors.
  • Parallel hierarchy of visitors has to be created for all members in visitable class hierarchy.

Credits