Respecting Open Closed Principle with Visitor Pattern

Posted on Monday, January 31, 2011

0


One of the common principles in the bouquet of SOLID principles is the Open Closed Principle (OCP) which states that software entities should be open for extension but closed for modification. This means that if we have to add any new functionality then ideally, we should be able to extend the current set of software entities by adding a new entity rather than creating a new one. In this post, I would quickly walk you through a situation in which the visitor pattern was helpful for us to make the code OCP compliant.

In our situation, we had three entities CarHistory, TyreHistory and BatteryHistory. Each of these three entities had a similar set of methods for different situations. For example, they required to generate XML for Insurance companies, Road Transport Organization and Car Manufacturers. So the class diagram looked something like this

The sample code for the TyreHistory class was

public class TyreHistory {

	public String toXMLForManufacturingCompany(){
		return null;
	}

	public String toXMLForInsuranceCompany(){
		return "Tyre Insurance records";
	}

	public String toXMLForRTO(){
		return null;
	}
}

A sample client for getting all the XML’s for the insurance company would look like this

public class Client {

	public static void main(String[] args) {
		Client c = new Client();
		c.getXMLForInsurance();
	}

	public String getXMLForInsurance(){
		StringBuffer sbf = new StringBuffer();
		sbf.append(new CarHistory().toXMLForInsuranceCompany()).append(" && ");
		sbf.append(new TyreHistory().toXMLForInsuranceCompany()).append(" && ");
		sbf.append(new BatteryHistory().toXMLForInsuranceCompany());
		System.out.println(sbf);
		return sbf.toString();
	}
}

As you would notice, that if we had to prepare XML for another agency, say the Audit department then all our three entities would have to change. This thoroughly violates the OCP. In order to counter this we used the visitor pattern.

The idea behind this pattern was that, now all the entities would accept the visitor and the visitor would be responsible for generating the particular XML. Let us see how the class diagram looked now

As you would notice, the new class diagram has only accept(visitor) method for all the entities. The main logic to create the XML for a particular requirement lies with the visitor. Let us see how the modified code looks like

public class TyreHistoryRefactored {

	public String accept(Visitor visitor){
		return visitor.visitTyre();
	}
}
public class ClientRefactored {

	public static void main(String[] args) {
		ClientRefactored c = new ClientRefactored();
		c.getXMLForInsurance();
	}

	public String getXMLForInsurance(){
		StringBuffer sbf = new StringBuffer();
		Visitor visitor = new InsuranceVisitor();
		sbf.append(new CarHistoryRefactored().accept(visitor)).append(" && ");
		sbf.append(new TyreHistoryRefactored().accept(visitor)).append(" && ");
		sbf.append(new BatteryHistoryRefactored().accept(visitor));
		System.out.println(sbf);
		return sbf.toString();
	}
}

As you would notice that the TyreHistory class becomes very simple now. It just accepts the visitor and calls the visitTyre method on the visitor. It is not concerned with the logic of generating the XML, neither does it care about what is the visitor implementation till the time that it is a visitor. Let us see how does the visitor look like

public interface Visitor {

	public String visitCar();

	public String visitTyre();

	public String visitBattery();
}

and a possible implementation would look like

public class InsuranceVisitor implements Visitor {

	@Override
	public String visitBattery() {
		return "Battery Insurance Records";
	}

	@Override
	public String visitCar() {
		return "Car Insurance Records";
	}

	@Override
	public String visitTyre() {
		return "Tyre Insurance Records";
	}
}

The advantages are multi-fold

  1. The entities are unaware of how the XML is being formed for a particular department. Tomorrow if there are any changes to the format then the entities do not change.
  2. All the formatting related to a particular agency, be it the Insurance or RTO lies within that visitor and is not spread across
  3. And, most important thing is that now, the addition of a new agency say the audit department does not involve any changes to the existing logic. The only change is extension i.e. a new visitor implementation called AuditVisitor is added and that has all the logic for generating XML for the audit department.

Thus through effective use of the visitor pattern we were able to respect the OCP and also keep our code clean.

Advertisements
Posted in: Architecture, Java