BufferedDev

A system’s design is rigid when a change in requirements leads to a cascade of changes in the software. The open closed principle is the antidote for rigid software designs, when implemented properly it makes the software system flexible. It’s the principle you should know.

The principle states that software entities — modules, classes, functions–  should be open for extension but closed for modification.

That means you should be able to add new features to a module that conforms to this principle without modifying the module’s source code or binaries. New features are added by adding new code and not by modifying existing working code. I know, this is beginning to sound like a riddle, how can you add new features without modifying existing code?

Let me show you.

Assuming we have a program that gets data from PDF files. The program has Client and PDFReader classes.

class PDFReader {
	readPDF(){
	   // reads pdf
        }
}


class Client {
   getData() {
     pdfReader = PDFReader()
     return pdfReader.readPDF()
   }
}

The Client class uses the PDFReader class to get the content of some pdf file.

You get your paycheck and just when you are about to retire early in some island, your program manager tells you that customers are about to cancel your product because they can’t read files from the internet! There is no way to add the new feature — reading files from the internet — without modifying the Client class. It violates the open closed principle.

To conform to the principle we need to add a layer of abstraction. Since the  client is concerned with  reading data from some source. Let’s put that concern in an interface.

interface ClientInterface {
   read()
}

All readers need to implement this interface.

class PDFReader implements ClientInterface {
      read() {
         // reads from pdf
       }
}

class NetworkReader implements ClientInterface {
     read() {
        // reads from the internet
     }
}

Then we update our Client to use the abstraction instead of the concrete classes.

class Client {
   	getData(reader: ClientInterface) {
         return reader.read()
     }
}

Now the client is open for extension but closed for modification. If we get a new requirement to read from a database, the Reader class will just have to implement the ClientInterface and alas, we have a new behaviour with zero modification on the Client class. It conforms to the open closed principle.

More important than applying the principle is knowing when to apply it. Conforming to the open closed principle is expensive as it takes effort and the layer of abstraction makes the design more complex. The principle should be limited to parts of the system that are likely to change.

View Comments

There are currently no comments.

Next Post