Respecting Open Closed Principle with Visitor Pattern

Table of contents
Reading Time: 3 minutes

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

[sourcecode language=”java”]
public class TyreHistory {

public String toXMLForManufacturingCompany(){
return null;
}

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

public String toXMLForRTO(){
return null;
}
}
[/sourcecode]

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

[sourcecode language=”java”]
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();
}
}
[/sourcecode]

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

[sourcecode language=”java”]
public class TyreHistoryRefactored {

public String accept(Visitor visitor){
return visitor.visitTyre();
}
}
[/sourcecode]

[sourcecode language=”java”]
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();
}
}
[/sourcecode]

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

[sourcecode language=”java”]
public interface Visitor {

public String visitCar();

public String visitTyre();

public String visitBattery();
}
[/sourcecode]

and a possible implementation would look like

[sourcecode language=”java”]
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";
}
}
[/sourcecode]

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.

Written by 

Vikas is the CEO and Co-Founder of Knoldus Inc. 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). Vikas has been working in the cutting edge tech industry for 20+ years. He was an ardent fan of Java with multiple high load enterprise systems to boast of till he met Scala. His current passions include utilizing the power of Scala, Akka and Play to make Reactive and Big Data systems for niche startups and enterprises who would like to change the way software is developed. To know more, send a mail to hello@knoldus.com or visit www.knoldus.com

4 thoughts on “Respecting Open Closed Principle with Visitor Pattern4 min read

  1. 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.

    1. 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. 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.

Comments are closed.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading