Friday, May 20, 2011

Open Close Principle ( OCP )

The Open-Closed Principle lies at the heart of the object-oriented paradigm. In a sense, we can say that its the goal of our object-oriented designs. Examining a myriad of patterns reveals its application often. It states the following:
Software entities should be open for extension, but closed for modification.

So it has 2 attributes:
  1. Open For Extension - It is possible to extend the behavior of the module as the requirements of the application change (i.e. change the behavior of the module).So classes can be extended
  2. Closed For Modification - Extending the behavior of the module does not result in the changing of the source code or binary code of the module itself.

Example - Violation of OCP
Consider the classes hierarchy:

class Animal
{
     protected String sound;
}

class Cat extends Animal
{
      //getters and setters
}

class Dog extends Animal
{
    //getters and setters
}

Now consider the class, which manages the animals and make them sound.
SoundManager.java
public class SoundManager{
    private List<Animal> animalList;
        
    public SoundManager(){
        animalList = new ArrayList<Animal>();
    }

    public void add(Animal a){
        animalList.add(a);
    }
    public void makeSound(){
        for(Animal a : animalList)
            if(a instanceof Cat)
                makeMew(a);
            else if(a instanceof Dog)
                makeBark(a);
    }
    public void makeMew(Animal a){
        print(a.getSound());
    }
    public void makeBark(Animal a){
        print(a.getSound());
    }

}
Note that this is not a good solution because suppose a new class Lion comes into picture, and it has a sound called roar, you have to edit the code and another if else or switch() to make that work. So it is violation of OCP.

Solution
So a good solution would be to make makeSound function part of class hierarchy itself.

class Animal
{
     protected String sound;
     public abstract void makeSound();  
}

class Cat extends Animal
{
      //getters and setters
     @Override
     public void makeSound(){
          print("mew");
     }
}

class Dog extends Animal
{
    //getters and setters
     @Override
     public void makeSound(){
          print("bark");
     }
}

Now it is much robust to write the above code, no matter how many animals you add.
public class SoundManager{
    private List<Animal> animalList;
        
    public SoundManager(){
        animalList = new ArrayList<Animal>();
    }

    public void add(Animal a){
        animalList.add(a);
    }
    public void makeSound(){
        for(Animal a : animalList)
            a.makeSound();
    }
    //makeBark, makeMew() functionr removed

}

Note of caution
It sems from the 80s and 90s when OO frameworks were built on the principle that everything must inherit from something else and that everything should be subclassable. So for example take a class called Stack in java, it subclasses Vector. So why on earth will you need push and pop if you can directly access element's index?
Joshua Bloch in his famous book "Effective Java" gives the following advice: "Design and document for inheritance, or else prohibit it", and encourages programmers to use the "final" modifier to prohibit subclassing.
So keep in mind, use inheritance only when its compulsory, otherwise try to make your classes final if you feel in future you won't be extending them.

Summary
It should be clear that no significant program can be 100% closed. So we may need to change the code depending on the requirement, but we can try to make our code as robust as possible. Though it comes with experience.

No comments:

Post a Comment

Chitika