Visitor Examples:
To demonstrate the visitor pattern in C#, we're going to take a little spin on the regular use of the pattern.
We're retelling the story of the Greek Hero Heracles, as he finds himself facing off against the seven-headed Hydra, and Cerberus, the guardian of the underworld.
To help simulate the conflict, we'll be creating an attack-type matrix using double-dispatch. This allows us to easily see how effective attacks from two different opponents are. For instance, we usually associate water as being effective against a fire; in our simulated conflict, we can then say that the Hydra might have an edge over Cerberus for those same reasons. This is a popular game mechanic that you might recognize as being similar to the <a href="https://pokemondb.net/type" style="color:inherit;" target="_blank">move effectiveness chart</a> from the Pokemon video games.
// In this example, we'll be doing things a little differently:
// Our base class with be both the visitor and the visitee!
// Additionally, we'll be implementing it as an abstract class
// although, you should take great care when using an abstract class for the visitor pattern!
public abstract class ElementalType
{
protected const float EFFECTIVE = 2.0f;
protected const float NOT_EFFECTIVE = 0.25f;
// Our base accept visitation method:
public abstract float Attack(ElementalType defenceType);
// ===================
// Elemental types:
// ===================
// The methods are virtual, which makes them optional.
// This way, implementing classes only need to have methods for special values:
public virtual float GetAttackMultiplier(WaterType attackType) { return 1.0f; }
public virtual float GetAttackMultiplier(FireType attackType) { return 1.0f; }
public virtual float GetAttackMultiplier(NormalType attackType) { return 1.0f; }
}
// Our water class:
public class WaterType: ElementalType
{
// Base accept method:
public override float Attack(ElementalType defenceType)
{
return defenceType.GetAttackMultiplier(this);
}
// We want to override Fire, Water, and Normal types:
public override float GetAttackMultiplier(FireType attackType)
{
// A Fire move against water is not effective:
return this.NOT_EFFECTIVE;
}
public override float GetAttackMultiplier(WaterType attackType)
{
// A water move against water is not effective:
return this.NOT_EFFECTIVE;
}
public override float GetAttackMultiplier(NormalType attackType)
{
// A normal move against water is effective:
return this.EFFECTIVE;
}
}
public class FireType: ElementalType
{
// Base accept method:
public override float Attack(ElementalType defenceType)
{
return defenceType.GetAttackMultiplier(this);
}
// We want to override Fire and Water types:
public override float GetAttackMultiplier(FireType attackType)
{
// A Fire move against fire is not effective:
return this.NOT_EFFECTIVE;
}
public override float GetAttackMultiplier(WaterType attackType)
{
// A water move against fire is effective:
return this.EFFECTIVE;
}
}
public class NormalType: ElementalType
{
// Base accept method:
public override float Attack(ElementalType defenceType)
{
return defenceType.GetAttackMultiplier(this);
}
// We want to override Fire and Water types:
public override float GetAttackMultiplier(FireType attackType)
{
// A Fire move against normal is effective:
return this.EFFECTIVE;
}
public override float GetAttackMultiplier(WaterType attackType)
{
// A water move against normal is not effective:
return this.NOT_EFFECTIVE;
}
}
using System;
public class Solution
{
public static void main(string[] args)
{
// Create our combatants:
ElementalType cerberus = new FireType();
ElementalType hydra = new WaterType();
ElementalType heracles = new NormalType();
// Cerberus attacks Hydra:
float multiplier = cerberus.Attack(hydra);
Console.WriteLine("Cerberus attacks Hydra and deals " + multiplier + " x damage!");
// Hydra attacks back:
multiplier = hydra.Attack(cerberus);
Console.WriteLine("Hydra attacks back and deals " + multiplier + " x damage!");
// Heracles attacks Hydra:
multiplier = heracles.Attack(hydra);
Console.WriteLine("Heracles attacks Hydra and deals " + multiplier + " x damage!");
// Cerberus attacks Heracles:
multiplier = cerberus.Attack(heracles);
Console.WriteLine("Cerberus attacks Heracles and deals " + multiplier + " x damage!");
Console.WriteLine("The battle has ended!");
}
}
Find any bugs in the code? let us know!