Wednesday, November 19, 2008

Open Closed Principle and Visitor pattern implementation in C#

“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification” – one of the basic principles of OOAD states.

This is especially valuable in a production environment, where changes to source code may necessitate code reviews, unit tests, and other such procedures to qualify it for use in a product: code obeying the principle doesn't change when it is extended, and therefore needs no such effort. Use of abstracted interfaces, where the implementations can be changed and multiple implementations could be created and polymorphic ally substituted for each other is normally followed as an approach for OCP.

In this article I will explain the Visitor* pattern implementation as a solution approach for the OCP.

* “The Visitor pattern defines and performs new operations on all the elements of an existing structure, without altering its classes”

The implementation of Visitor pattern involves two interfaces/ abstract classes (IVisitor and IElement) as shown in the class diagram below




The Visitor pattern has two distinct parts: there are the classes that make up an object structure, and then there are the methods that will be applied to the object structure. These are the visitors. The object structure is specified by a hierarchy of Element classes.

In my example the Visitors section is defined as

public interface IVisitor

{

void Visit(Wheel wheel);

void Visit(Engine engine);

void Visit(Body body);

void VisitCar(Car car);

}

class PrintVisitor : IVisitor

{

public void Visit(Wheel wheel)

{

Console.WriteLine("Visiting " + wheel.Name + " wheel");

}

public void Visit(Engine engine)

{

Console.WriteLine("Visiting engine");

}

public void Visit(Body body)

{

Console.WriteLine("Visiting body");

}

public void VisitCar(Car car)

{

Console.WriteLine("Visiting Car");

foreach (IElement element in car.carElements)

{

element.Accept(this);

}

Console.WriteLine("Finished Visiting");

}

}

The Elements are classes implementing the IElement interface.

public interface IElement

{

void Accept(IVisitor visitor);

}

public class Car

{

public List<IElement> carElements = new List<IElement>{

new Wheel { Name= "Front left" },

new Wheel { Name= "Front right" },

new Wheel { Name= "Back left" },

new Wheel { Name= "Back right" },

new Engine (),

new Body ()

};

}

public class Wheel : IElement

{

public string Name { get; set; }

public Wheel () { }

public Wheel (string name)

{

this.Name = name;

}

#region IElement Members

public void Accept(IVisitor visitor)

{

visitor.Visit(this);

}

#endregion

public override string ToString()

{

return "Wheel";

}

}

public class Engine : IElement

{

#region IElement Members

public void Accept(IVisitor visitor)

{

visitor.Visit(this);

}

#endregion

public override string ToString()

{

return "Engine";

}

}

public class Body : IElement

{

#region IElement Members

public void Accept(IVisitor visitor)

{

visitor.Visit(this);

}

#endregion

public override string ToString()

{

return "Body";

}

}

The client code that uses the Visitor pattern is implemented as

Car car = new Car();

PrintVisitor visitor = new PrintVisitor();

visitor.VisitCar(car);

In the next series of this post I will explain the creation and usage of a generic Visitor implementation using delegates and anonymous methods.


No comments: