Schrödingers cat was a big part of this.

Introduction

The second article in the SOLID article series will deal with the O part. We are going to have a look at the Open/Closed Principle. From now on called OCP. The OCP is all about introducing new features to existing code and how to not change your code all the time. Originally this principle states:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Basically what this means is that if a new feature should be introduced you should never change your already written code in a way that you have to change it again for the next feature. Only write new code and let the old code use the new classes or methods. In pratice, this is easier said than done because most of the time you do not know what will change or what new features will be coming. But the moment you know you can make sure to do it the right way. Nuff talking, more coding. Let me give you a quick example of how not to do it.

The Problem with this problem

Lets assume we are interested in getting the value of some stocks we bought a couple of months ago. We bought some stocks of NeatCode and Langhorst Inc. Both of which are doing great by the way. How great? Ask the StockValueService:

[pastacode lang=”java” manual=”public%20class%20StockValueService%20%7B%0A%09%0A%09public%20Float%20getCurrentStockValue(String%20stockname)%20%7B%0A%09%09if%20(%22Neat%20Code%22.equals(stockname))%20%7B%0A%09%09%09return%20getCurrentNeatCodeValue()%3B%0A%09%09%7D%20else%20if(%22Langhorst%20Inc.%22.equals(stockname))%20%7B%0A%09%09%09return%20getCurrentLanghorstIncValue()%3B%0A%09%09%7D%20else%20%7B%0A%09%09%09throw%20new%20IllegalArgumentException(%22Unable%20to%20get%20value%20for%20stock%3A%20%22%2Bstockname)%3B%0A%09%09%7D%0A%09%09%0A%09%7D%0A%09%0A%09private%20Float%20getCurrentNeatCodeValue()%20%7B%0A%09%09%2F%2F%20get%20data%20%0A%09%09return%20new%20Float(1600.25)%3B%0A%09%7D%0A%09%0A%09private%20Float%20getCurrentLanghorstIncValue()%20%7B%0A%09%09%2F%2F%20get%20data%20%0A%09%09return%20new%20Float(981.19)%3B%0A%09%7D%0A%0A%7D” message=”” highlight=”” provider=”manual”/]

So, what to do if your brain is full of ideas and two companies ain’t enough. Exactly. Third company it is: TL Corp. Now let us find out how much it is worth (after only a couple of seconds).

[pastacode lang=”java” manual=”public%20class%20StockValueService%20%7B%0A%0A%09public%20Float%20getCurrentStockValue(String%20stockname)%20%7B%0A%09%09if%20(%22Neat%20Code%22.equals(stockname))%20%7B%0A%09%09%09return%20getCurrentNeatCodeValue()%3B%0A%09%09%7D%20else%20if%20(%22Langhorst%20Inc.%22.equals(stockname))%20%7B%0A%09%09%09return%20getCurrentLanghorstIncValue()%3B%0A%09%09%7D%20else%20if%20(%22TL%20Corp.%22.equals(stockname))%20%7B%0A%09%09%09return%20getCurrentTlCorpValue()%3B%0A%09%09%7D%20else%20%7B%0A%09%09%09throw%20new%20IllegalArgumentException(%22Unable%20to%20get%20value%20for%20stock%3A%20%22%20%2B%20stockname)%3B%0A%09%09%7D%0A%0A%09%7D%0A%0A%09private%20Float%20getCurrentNeatCodeValue()%20%7B%0A%09%09%2F%2F%20get%20data%0A%09%09return%20new%20Float(1600.25)%3B%0A%09%7D%0A%0A%09private%20Float%20getCurrentLanghorstIncValue()%20%7B%0A%09%09%2F%2F%20get%20data%0A%09%09return%20new%20Float(981.19)%3B%0A%09%7D%0A%0A%09private%20Float%20getCurrentTlCorpValue()%20%7B%0A%09%09%2F%2F%20get%20data%0A%09%09return%20new%20Float(1205.70)%3B%0A%09%7D%0A%0A%7D” message=”” highlight=”” provider=”manual”/]

Is this class open for extension? Yes it is. Is this class closed for modification. Definitely not! Every time you open up or wear down a company you have to change your class. How can we solve this problem? As most of the time inheritance is king at solving this problem.

Refactoring baby

Much like Batman, who is the answer to all the problems of Gotham City, inheritance is the answer to most problems of object oriented programming. First of all let us define an interface

[pastacode lang=”java” manual=”public%20interface%20StockValueProvider%20%7B%0A%09public%20Float%20getCurrentStockValue()%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

Instead of passing the stock name as an argument to the getCurrentStockValue() method let us directly pass an instance of the StockValueProvider interface and call it within.

[pastacode lang=”java” manual=”public%20class%20StockValueService%20%7B%0A%0A%09public%20Float%20getCurrentStockValue(StockValueProvider%20stockValueProvider)%20%7B%0A%09%09return%20stockValueProvider.getCurrentStockValue()%3B%0A%09%7D%0A%0A%7D” message=”” highlight=”” provider=”manual”/]

To actually get some values we need to implement the StockValueProvider interface for each Stock we want to get the values for.

[pastacode lang=”java” manual=”public%20class%20LanghorstIncStockValueProvider%20implements%20StockValueProvider%20%7B%0A%0A%09%40Override%0A%09public%20Float%20getCurrentStockValue()%20%7B%0A%09%09%2F%2F%20get%20data%0A%09%09return%20new%20Float(981.19)%3B%0A%09%7D%0A%0A%7D” message=”” highlight=”” provider=”manual”/]

[pastacode lang=”java” manual=”public%20class%20NeatCodeStockValueProvider%20implements%20StockValueProvider%7B%0A%0A%09%40Override%0A%09public%20Float%20getCurrentStockValue()%20%7B%0A%09%09%2F%2F%20get%20data%20%0A%09%09return%20new%20Float(1600.25)%3B%0A%09%7D%0A%0A%7D” message=”” highlight=”” provider=”manual”/]

[pastacode lang=”java” manual=”public%20class%20TlCorpStockValueProvider%20implements%20StockValueProvider%20%7B%0A%0A%09%40Override%0A%09public%20Float%20getCurrentStockValue()%20%7B%0A%09%09%2F%2F%20get%20data%0A%09%09return%20new%20Float(1205.70)%3B%0A%09%7D%0A%0A%7D%0A” message=”” highlight=”” provider=”manual”/]

In order to call them we just pass instances of StockValueProvider implementations into the getCurrentStockValue() method.

[pastacode lang=”java” manual=”public%20class%20Main%20%7B%0A%0A%09public%20static%20void%20main(String%5B%5D%20args)%20%7B%0A%0A%09%09StockValueService%20stockService%20%3D%20new%20StockValueService()%3B%0A%0A%09%09Float%20valueNc%20%3D%20stockService.getCurrentStockValue(new%20NeatCodeStockValueProvider())%3B%0A%09%09System.out.println(%22The%20current%20value%20Neat%20Code%20is%3A%20%22%20%2B%20valueNc)%3B%0A%0A%09%09Float%20valueLanghorst%20%3D%20stockService.getCurrentStockValue(new%20LanghorstIncStockValueProvider())%3B%0A%09%09System.out.println(%22The%20current%20value%20Langhorst%20Inc.%20is%3A%20%22%20%2B%20valueLanghorst)%3B%0A%09%09%0A%09%09Float%20valueTlCorp%20%3D%20stockService.getCurrentStockValue(new%20TlCorpStockValueProvider())%3B%0A%09%09System.out.println(%22The%20current%20value%20TL%20Corp%20is%3A%20%22%20%2B%20valueTlCorp)%3B%0A%09%09%09%0A%09%7D%0A%0A%7D” message=”” highlight=”” provider=”manual”/]

If anything changes we simply need to write more or delete existing implementations of the StockValueProvider interface without touching our StockValueService.class. Want to provide the value in Yen? Or Euro? Or pizza slices? Just implement some StockValueProvider classes and pass them into the getCurrentStockValue() method.

Open for extension: yes!
Closed for modification: yes!
Great success: yes!

Oh, and by the way. You just made use of the Strategy Design Pattern. Congratulations!

What have we learned?

Extracting the logic and putting it into separate implementations of one interface provides us with the capability of easily extending our classes without modifying its implementation. This is what the Open/Closed Principle is all about. We saw that we could easily extend the way we get stock information by putting the way of retrieving data into separate classes (SRP anyone?).

Next time we will find out how to get Batman really pissed by violating the Liskov Substitution Principle.

 

Categories: Blog Posts