Android O (O for OCP) :)

Android O — SOLID

Abdul Rahman
AndroidPub

--

‘O’ stands for Open close principle in SOLID principles. This is the follow up article of the previous blog.

In this article let us try to understand about Open Closed Principle — OCP. Like in any other development, android app development also evolves in terms of functionality very constantly and rapidly. If you have been wondering how to add a new functionalities to the app without much effort, please continue reading the article. This article should give you a fair understanding of What, Why, and How aspects of OCP.

Previously we discussed about Single Responsibility Principle (SRP) using a simple app, which has a single screen to track the calories. We discussed two approaches to follow SRP:

  1. Using fragments: CalorieInputFragment & CalorieOutputFragment
  2. Using custom views: CalorieInputView & CalorieOutputView

CalorieInputFragment/CalorieInputView had logic to get the input and convey back to activity. CalorieOutputFragment/CalorieOutputView had logic to output the tracked calories. Let us use the same example to understand about OCP.

Consider a scenario where we need to change the color of the progress bar when the calories input is greater than 500. Does that seems to be an easy change to make? We can simply go ahead and change the setCalorieProgress method in CalorieOutputFragment to achieve this.

If we need to update the progress bar color corresponding to calorie value, we would end up adding couple of more if conditions. Thus, We constantly change CalorieOutputFragment.

Setting the problem statement: what will we do in order to add functionalities without actually modifying the code?

To answer that, let us understand OCP:

Open/Closed Principle is the most important of all principles in SOLID. OCP also abbreviated as Open for extension and Closed for modification. It says that a module/class should be designed in such a way that, we need to extend the class to add new features and not modify it.

Meyer, Bertrand explains OCP as:

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

Why OCP?

Assume we have lots of logic in CalorieOutputFragment.

  1. If we decide that new functionalities are added using a feature toggle, we need to maintain the existing functionality and not change CalorieOutputFragment.
  2. Maintainability of CalorieOutputFragment will also become difficult if the class constantly changes.
  3. While working in a team, changing the same file constantly hinders parallel development.

There are various other scenarios where we may not want to change the code of a particular module/class but want to add new behaviors to it. If we don’t adhere to OCP, turn around time to add new features will increase.

How to adhere to OCP?

If we design carefully by keeping in mind that any software entity is subject to constant change, we can close a particular class/module.

For instance: If we kept in mind, the progress bar color may change depending upon the calorie value, while designing we could have passed a Calorie object. This object will give us the color instead of passing the int calories. Thus making CalorieOutputFragment closed for modification.

In the above code snippet, Calorie class is not closed, it’s still open for modification for adding a new color. Since createCalorieEnumFor method acts like a Factory method, it can be open for adding logic for creation of new objects. In the end of the article let us also discuss about what violates and what doesn’t violate OCP.

Note: Above example is considered as strategy pattern.

Let us take another example: If we look at the code of CalorieTrackerActivity, it implements an interface CalorieManager.

And CalorieInputFragment uses the reference of the interface to communicate to the Activity.

If the code in onCalorieSubmit method was like this:

Then CalorieInputFragment would have not been closed even for a small change where the fragment needs to be used in another activity. This is another example of OCP.

On the same line of thoughts, we need to modify CalorieTrackerActivity to replace CalorieInputFragment or CalorieOutputFragment, thus it isn’t closed. As it holds a reference of those fragments. So to adhere to OCP, we can change the code like this:

By doing this, if we want to replace the CalorieOutputFragment, we just need to make our fragment extend the abstract class OutputFragment and inject the object into the CalorieTrackerActivity.

In both the example discussed, we have brought the classes to Strategic closure. Which means we have identified use cases for which our classes will be closed for modification, and can be easily extended. It’s important to note that it is not possible to make our class closed for all use cases.

Having discussed on how to follow OCP, let us understand coding practices that violates OCP:

  1. Run time type identification (RTTI) : These are our type casts which we do in order to call a method in that object. This can be either avoided using Event based approach (Like RxJava) or can be avoided by defining a contract, where we create an interface/abstract class and use the abstraction to call the methods.
  2. If else block over polymorphism : These are the areas where we use the conditions to decide the flow. Which could have been easily done using Inversion of control design pattern. Ref Martin Fowler’s example.
  3. Having public class variables : If we want to change few class variables, since it’s not private, other areas in the code would have referenced it. Hence to make a change we need to change all the places of reference. Even a type change will be difficult.

Conclusion:

Following OCP guarantees:

  1. Decrease in turn around time for a new feature addition.
  2. Better maintainability.
  3. Highly re-usable components.

Few practices that make code violate OCP:

  1. If else ladder used to define flows.
  2. Forced type casting, for example: (SomeActivity) context.
  3. Making class variables public.
  4. Holding reference of specific fragments instead of abstraction.

In general, to add a new feature if we go and modify the existing code, we can understand that we have not made the class closed for that use case.

Use cases at least for which OCP should be followed:

  1. Reusing the fragment in any other activity.
  2. Replacing the fragment in an activity.
  3. Renaming a variable in a specific class shouldn’t make you change lot of other classes.
  4. View related changes like changing Linear layout into Relative layout. Thus view related things shouldn’t be done programmatically. So next time don’t search like: “How to set height of linear layout programmatically?” 😄

Please clap if you liked the article. Feel free to share any suggestions.

--

--