Visitor pattern
2023-02-10 C# Visitor Design Patterns Accept VisitThe Visitor design pattern is technique to separate an algorithm from object on which it operates. In essence the visitor allows adding new functions to the family of classes without modifying them. Somehow similar approach can be done with patterns mentioned in previous post.
Let’s define few interfaces first
public interface IActionVisitable
{
void Accept(IActionVisitor impl);
}
public interface IActionVisitor
{
void Visit(CloseWithoutHireAction action);
void Visit(RenameAction action);
void Visit(SetStartDateAction action);
}
First is used to define an action, second it used to define the visitor, where variants of Visit
method is defined for all actions. In my case, the actions are operation on database that store information on hiring. The Action
keeps data and provides Accept
method that dispatch to the appropriate method in supplied IActionVisitor
class
public class CloseWithoutHireAction : IActionVisitable
{
public string Manager { get; }
public string Req { get; }
public CloseWithoutHireAction(string manager, string req)
{
Manager = manager;
Req = req;
}
public void Accept(IActionVisitor visit) => visit.Visit(this);
}
The Visitor can look like this
/// <summary>
/// Validates that the actions requested can be done and the initial assumptions
/// are correct.
/// </summary>
public class ValidateActions : IActionVisitor
{
// ...
public void Visit(CloseWithoutHireAction action)
{
log.Info($"Validating {action.GetType().Name}: {action.Manager} {action.Req}");
ReqShouldExist(action.Manager, action.Req);
}
public void Visit(RenameAction action)
{
// ...
}
}
Usage is pretty simple
List<IActionVisitable> actions = new()
{
new CloseWithoutHireAction("A manager", "HRD114277"),
new RenameAction("Another manager", "TBD_00000875", "HRD166032"),
// ...
};
ValidateActions validator = new ValidateActions(Database);
foreach (var a in actions)
a.Accept(validator);
if (validator.AllOk)
{
log.Info("Everything is OK");
DatabaseActions databaseUpdate = new DatabaseActions(Database);
foreach (var a in actions)
a.Accept(databaseUpdate);
}
else
{
log.Warn("There was a problem, skipping database updates");
}
It all is quite simple, especially since you have your interfaces, the Visual Studio refactoring tools make it quite easy to build new actions. Once they are added into IActionVisitor
, refactoring offers adding Visit
method to all visitors. They in turn can benefit from shared infrastructure. In my case above it the method ReqShouldExist
is used by Visit
method for all actions.