Saturday, February 4, 2012

OOD Principles : Part 1 - Open-to-Close Principle (OCP)

Overview:
This post will look at Open-to-Close Principle(OCP) with examples. The examples tries to describe and compare the design complications when not abide by the principle.

What is OCP?
It states, the design should be as flexible as possible such that new features/functionality could be added with zero-to-minimum changes to the existing code. In other words, the design should allow the introduction of new functionality as new classes/components, keeping as much as possible existing code intact and unchanged.

Idea behind:
Software modules and functionality should be open for extension, but closed for modifications. Decorator and Factory Method patterns yeilds us confined to Open Close principle.

Firstly, lets look at a bad example of a design that violates OCP. This will help us to better understand the idea behind the principle.

Consider a Repository that stores SOA entities such as Services, Wsdls, Schemas and Policies. The Repository is responsible for processing these artifacts.

Bad Example of Open-to-Close Principle.

In intuitive design for this could be the following,



The corresponding implementation,
 /**
* Example of a bad design - violates Open-to-close principle.
*
*/
public interface RepositoryArtifact {
String getURL();
void setURL(String url);
}
public class Schema implements RepositoryArtifact {
private String url;
public String getURL() {
return url;
}
public void setURL(String url) {
this.url = url;
}
}
public class Wsdl implements RepositoryArtifact {
private String url;
public String getURL() {
return url;
}
public void setURL(String url) {
this.url = url;
}
}
public class Repository {
/**
* Introducing a new repository artifact type (e.i: Policy) would need the processRepositoryArtifact method to be
* changed to handle the new type. Also, need to add a new method (e.i: processPolicy) to handle the processing code.
*
* @param repositoryArtifact
*/
public void processRepositoryArtifact(RepositoryArtifact repositoryArtifact) {
if(repositoryArtifact instanceof Wsdl) {
processWsdl((Wsdl) repositoryArtifact);
} else if (repositoryArtifact instanceof Schema) {
processSchema((Schema) repositoryArtifact);
}
}
private void processWsdl(Wsdl wsdl) {
// code to process a wsdl
}
private void processSchema(Schema schema) {
// code to process a schema
}
}
It's said that this is a bad example of OCP. The reason is introducing a new registry artifact type (e.i: Policy) would need the processRegistryArtifact method to be changed to handle the new type. Also, need to add a new method (e.i: processPolicy) to handle the processing code. Similar to following,
 public class Policy implements RepositoryArtifact {
. . .
}
Updated Repository class to handle the processing of Policy types.
public class Repository {
public void processRepositoryArtifact(RegistryArtifact repositoryArtifact) {
if(repositoryArtifact instanceof Wsdl) {
processWsdl((Wsdl) repositoryArtifact);
} else if (repositoryArtifact instanceof Schema) {
processSchema((Schema) repositoryArtifact);
} else if ( repositoryArtifact instanceof Policy) {
processPolicy((Policy) repositoryArtifact);
}
}
. . .
private void processPolicy(Policy policy) {
// code to process a policy
}
}
So, as you can see, trying to introduce a new type causes us to make changes to the Repository module, and hence, this is a very bad design.

Good Example of Open-to-Close Principle.
Let's how to make this compliant with OCP. The revised class diagram is as follows,


Corresponding implementation,
 public interface RegistryArtifact { 
String getURL();
void setURL(String url);
boolean process();
}
public class Wsdl implements RegistryArtifact {
private String url;
public String getURL() {
return url;
}
public void setURL(String url) {
this.url = url;
}
public boolean process() {
// code to process a wsdl
return true;
}
}
public class Schema implements RegistryArtifact {
private String url;
public String getURL() {
return url;
}
public void setURL(String url) {
this.url = url;
}
public boolean process() {
// code to process a schema
return true;
}
}
public class Repository {
/**
* Introducing a new registry artifact type (e.i: Policy) would not require any changes now.
*
* @param repositoryArtifact
*/
public void process(RegistryArtifact repositoryArtifact) {
repositoryArtifact.process();
}
}
Here, each artifact will have the implementation of the logic how to handle them. According to the new design, new types can be easily plugged without having to change the existing code.
 public class Policy implements RegistryArtifact { 
. . .
public boolean process() {
// code to process a policy
return true;
}
}
Summary:
OCP is an important principle that helps and guides us to do a clean and flexible OO Design.