Visitor Pattern
Pros
- Allows Double Dispatching
Decouples data structures from operations performed on them while also allowing those operations to be dynamically changed
Does not break the open/closed principle
Cons
- When a new visitable class is created all visitors need to be modified so this pattern is better used for classes that won't change
Example Explanation
ElementalDamageVisitor - This class increases the damage of a Mob based on their element through visiting
FireMob/WaterMob/LightningMob - These classes use the visitor to increase their damage

import java.util.Random;
class Main {
public static void main(String[] args) {
ElementalDamageVisitor visitor = new ElementalDamageVisitor();
Mob fire = new FireMob();
System.out.println( fire.getAttack() );//10
fire.accept(visitor);
System.out.println( fire.getAttack() );//13
System.out.println();
Mob water = new WaterMob();
System.out.println( water.getAttack() );//10
water.accept(visitor);
System.out.println( water.getAttack() );//12
System.out.println();
Mob light = new LightningMob();
System.out.println( light.getAttack() );//10
light.accept(visitor);
System.out.println( light.getAttack() );//randon num between 11 and 15
}
}
enum Element {
FIRE,
WATER,
LIGHTNING
}
interface IVisitor{
public void visit(FireMob mob);
public void visit(WaterMob mob);
public void visit(LightningMob mob);
}
abstract class Mob {
protected Element elem;
private int attack;
public Mob(Element elem){
this.elem = elem;
this.attack = 10;
}
public Element getElement(){ return elem; }
public int getAttack(){ return attack; }
public void setAttack(int newAttack){ this.attack = newAttack; }
abstract void accept(IVisitor visitor);
}
class ElementalDamageVisitor implements IVisitor{
public void visit(FireMob mob){
//fire mobs get +3 attack
mob.setAttack( mob.getAttack() + 3 );
}
public void visit(WaterMob mob){
//water mobs get +2 attack
mob.setAttack( mob.getAttack() + 2 );
}
public void visit(LightningMob mob){
//lightning mobs get +1 to +5 attack
Random ran = new Random();
int dmgBoost = ran.nextInt(5) + 1;
mob.setAttack( mob.getAttack() + dmgBoost );
}
}
class FireMob extends Mob{
public FireMob(){
super(Element.FIRE);
}
@Override
public void accept(IVisitor visitor){visitor.visit(this);}
}
class WaterMob extends Mob{
public WaterMob(){
super(Element.WATER);
}
@Override
public void accept(IVisitor visitor){visitor.visit(this);}
}
class LightningMob extends Mob{
public LightningMob(){
super(Element.LIGHTNING);
}
@Override
public void accept(IVisitor visitor){visitor.visit(this);}
}
Extra Resources
https://www.tutorialspoint.com/design_pattern/visitor_pattern.htm
https://softwareengineering.stackexchange.com/a/333702/293317