Skip to main content

Visitor Pattern Example

A short refresher on using the Visitor pattern.

The Visitor pattern allows functionality to be added to a class without modifying the class.

It’s useful when you have a collection of classes (Elements) that may need additional groups of functionality. A Visitor can implement functionality that applies to a group of Elements, avoiding the need to change every Element.

A simple Visitor pattern in TypeScript:

// The Element
abstract class Elem {
  abstract accept<T>(visitor: Visitor<T>): T;
}

// The Visitor
interface Visitor<T> {
  visitHello(element: Hello): T;
  visitWorld(element: World): T;
}

// Elem implementations
class Hello extends Elem {
  readonly hello = "hello";

  accept<T>(visitor: Visitor<T>): T {
    return visitor.visitHello(this);
  }
}

class World extends Elem {
  readonly world = "world";

  accept<T>(visitor: Visitor<T>): T {
    return visitor.visitWorld(this);
  }
}

// Visitor implementation
// Create new Visitor implementations to apply different functionality to Elem classes
class HelloWorlder implements Visitor<string> {
  run(element: Elem) {
    return element.accept(this);
  }

  visitHello(element: Hello): string {
    return `visitHello says: ${element.hello.toUpperCase()} world!`;
  }

  visitWorld(element: World): string {
    return `visitWorld says: hello ${element.world.toUpperCase()}!`;
  }
}

// Usage
const worlder = new HelloWorlder();
console.log(worlder.run(new World()));
console.log(worlder.run(new Hello()));

Run on the TypeScript Playground

The above script will output:

visitWorld says: hello WORLD!
visitHello says: HELLO world!

References