WalzoneInterview Prep
📞 Interviewing soon? Practice with a realistic AI mock phone interview — it calls you, then scores you. First 15 min FREE →

Design Patterns · Advanced · question 60 of 100

How can you apply the Visitor pattern to a large object hierarchy with frequent additions of new element types while maintaining the Open/Closed Principle?

📕 Buy this interview preparation book: 100 Design Patterns questions & answers — PDF + EPUB for $5

The Visitor pattern can be used to traverse a complex object hierarchy and perform operations on its elements without modifying their classes. However, one challenge with this pattern is that it requires modifying the Visitor interface every time a new element type is added to the hierarchy. This violates the Open/Closed Principle, which states that classes should be open for extension but closed for modification.

To apply the Visitor pattern while maintaining the Open/Closed Principle, we can use the Double Dispatch technique. This technique involves creating a separate method for each combination of element type and operation in both the Visitor interface and the Element hierarchy. This allows new element types and operations to be added without modifying any existing code.

Consider the following example where we have a complex object hierarchy that represents mathematical expressions:

abstract class Expression {
    abstract double evaluate();
}

class Number extends Expression {
    double value;
    double evaluate() { return value; }
}

class BinaryOperation extends Expression {
    char operator;
    Expression left;
    Expression right;
    double evaluate() { ... }
}

Suppose we want to implement a ‘Printer‘ Visitor that prints out the expression in a human-readable format. Without Double Dispatch, we would need to modify the Visitor interface every time a new type of Expression is added:

interface Printer {
    void printNumber(Number number);
    void printBinaryOperation(BinaryOperation binaryOperation);
    // need to add new method for each new type of Expression
}

With Double Dispatch, we add a method for each combination of Expression type and Visitor operation in both the Visitor interface and the Element hierarchy:

interface Expression {
    void accept(Printer printer);
}

class Number implements Expression {
    double value;
    void accept(Printer printer) {
        printer.visitNumber(this);
    }
}

class BinaryOperation implements Expression {
    char operator;
    Expression left;
    Expression right;
    void accept(Printer printer) {
        printer.visitBinaryOperation(this);
    }
}

interface Printer {
    void visitNumber(Number number);
    void visitBinaryOperation(BinaryOperation binaryOperation);
}

class SimplePrinter implements Printer {
    void visitNumber(Number number) {
        System.out.print(number.value);
    }
    void visitBinaryOperation(BinaryOperation binaryOperation) {
        System.out.print("(");
        binaryOperation.left.accept(this);
        System.out.print(" " + binaryOperation.operator + " ");
        binaryOperation.right.accept(this);
        System.out.print(")");
    }
}

Now we can add new types of Expressions without modifying the Visitor interface or any existing code:

class UnaryOperation implements Expression {
    char operator;
    Expression expression;
    void accept(Printer printer) {
        printer.visitUnaryOperation(this);
    }
}

interface Printer {
    void visitNumber(Number number);
    void visitBinaryOperation(BinaryOperation binaryOperation);
    void visitUnaryOperation(UnaryOperation unaryOperation);
}

class SimplePrinter implements Printer {
    void visitNumber(Number number) {
        System.out.print(number.value);
    }
    void visitBinaryOperation(BinaryOperation binaryOperation) {
        System.out.print("(");
        binaryOperation.left.accept(this);
        System.out.print(" " + binaryOperation.operator + " ");
        binaryOperation.right.accept(this);
        System.out.print(")");
    }
    void visitUnaryOperation(UnaryOperation unaryOperation) {
        System.out.print("(" + unaryOperation.operator);
        unaryOperation.expression.accept(this);
        System.out.print(")");
    }
}

In summary, Double Dispatch allows us to apply the Visitor pattern to a large and evolving object hierarchy while maintaining the Open/Closed Principle by adding a method for each combination of Element type and Visitor operation in both the Visitor interface and the Element hierarchy.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Design Patterns interview — then scores it.
📞 Practice Design Patterns — free 15 min
📕 Buy this interview preparation book: 100 Design Patterns questions & answers — PDF + EPUB for $5

All 100 Design Patterns questions · All topics