Respecting Open Closed Principle with Visitor Pattern


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

About Vikas Hazrati

Vikas is the Founding Partner @ Knoldus which is a group of software industry veterans who have joined hands to add value to the art of software development. Knoldus does niche Reactive and Big Data product development on Scala, Spark and Functional Java. Knoldus has a strong focus on software craftsmanship which ensures high-quality software development. It partners with the best in the industry like Lightbend (Scala Ecosystem), Databricks (Spark Ecosystem), Confluent (Kafka) and Datastax (Cassandra). To know more, send a mail to hello@knoldus.com or visit www.knoldus.com
This entry was posted in Architecture, Java and tagged , , , . Bookmark the permalink.

4 Responses to Respecting Open Closed Principle with Visitor Pattern

  1. Jakub says:

    This still violates OCP:

    How would you add new functionality (like Engines history) without changing existing classes?

    If your visitors are to visit Engines, you’ll need to expand Visitor interface. And, because all the xxxxHistory classes (Tyre, Battery etc) relies on this interface, you need to modify existing code.

    • Jakub, you might have a point in the situation that you mention. However, no code what so ever would be OCP compliant in all situations that you might come up with. It would be OCP compliant in the given situation and you would try to make it OCP compliant with the known set of changes that you expect which would make the pattern break. Of course if you add environmental situations which are not foreseen at the moment then you cannot think about making the code compliant either. As a first rule never start by making your code OCP compliant. Make it OCP compliant when you see a change which would occur again and for which you have to modify your code. I hope that helps.

  2. Jakub says:

    Ok, my point was that the Visitor pattern is anti-OCP per se, because even if you can expand one set of involved classes (Visitors), the other set (Visitees) is closed to extension.

  3. All right point taken. In many circumstances you might be right but as with everything even the visitor pattern would be OCP compliant as in our particular situation.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s