Visitor

Separating algorithms from objects on which they operate

This pattern allows for introducing a new behavior without modifying a class. The new behavior is extracted to another class, and the object (that needs a new behavior) is passed to that new class to be worked on.
The code example below looks at home appliances. Sometimes they need to be repaired, but it is not their primary function. So now they need to implement a "repair me" functionality by being worked on, but adding that code to the appliance class violates the Single Responsibility Principle as a dishwasher must wash dishes and not repair itself. Hence, introducing an interface RepairPerson that can fix ovens, dishwashers and washing machines:
interface RepairPerson {
  fixWashingMachine(a: Appliance): void;
  fixDishwasher(a: Appliance): void;
  fixOven(a: Appliance): void;
}
Here is an class that implements RepairPerson interface:
class SuperService implements RepairPerson {
  fixWashingMachine(a: Appliance): void {
    console.log(
      'SuperService reports: "Attempting to fix a washing machine". \nNeed to drain water first, then unscrew the back and fix this washing machine'
    );
    a.work();
  }
  fixDishwasher(a: Appliance): void {
    console.log(
      'SuperService reports: "Attempting to fix a dishwasher". \n First, need to pull it out, then take it from there.'
    );
    a.work();
  }
  fixOven(a: Appliance): void {
    console.log(
      'SuperService reports: "Attempting to fix an oven". \nOh, seems like it was unplugeed. Easy repair!'
    );
    a.work();
  }
}
Each method takes an instance of a class that implements the Appliance interface, which is:
interface Appliance {
  work(): void;
  accept(repairPerson: RepairPerson): void; //functionality is separate because it's not a primary function of this object
}
And now the three appliances:
class WashingMachine implements Appliance {
  work(): void {
    console.log("washing machine is working");
  }
  normalCycle(): void {
    console.log("normal cycle");
  }
  accept(repairPerson: RepairPerson) {
    repairPerson.fixWashingMachine(this);
  }
}
class DishWasher implements Appliance {
  work(): void {
    console.log("Dishwasher is working");
  }
  accept(repairPerson: RepairPerson): void {
    repairPerson.fixDishwasher(this);
  }
}
class Oven implements Appliance {
  work(): void {
    console.log("Oven is working");
  }
  accept(repairPerson: RepairPerson): void {
    repairPerson.fixOven(this);
  }
}
Each appliance knows that in order to get fixed, it takes an instance of a RepairPerson, this is the actual Visitor. Then, appliance calls the appropriate method to request the required behavior. So on one hand, an appliance acquired new behavior, but on the other hand, that behavior is a part of another class and is maintained there, outside the actual appliance. Here is the setup code:
let fixItNow = new SuperService();
let wm = new WashingMachine();
wm.accept(fixItNow);
console.log();
let dw = new DishWasher();
dw.accept(fixItNow);
console.log();
let ov = new Oven();
ov.accept(fixItNow);
The result:
SuperService reports: "Attempting to fix a washing machine". 
Need to drain water first, then unscrew the back and fix this washing machine
washing machine is working

SuperService reports: "Attempting to fix a dishwasher".
 First, need to pull it out, then take it from there.
Dishwasher is working

SuperService reports: "Attempting to fix an oven".
Oh, seems like it was unplugeed. Easy repair!
Oven is working