Orthogonality in Object Oriented Design Principle

Today in this blog post I will discuss the orthogonality principle on object oriented design principles, why it's important and how we can avoid making changes in code that leads to breaking existing code. We will also see an example that will explain clearly what can go wrong if we don't follow this vital principle in object oriented design.

 

Contents

  1. What is Orthogonality ?

  2. Why do we need to have Orthogonality ?

  3. How to avoid writing non-Orthogonal code ?

  4. Example of non-Orthogonality

 

What is Orthogonality ?

The orthogonality principle says, two classes are orthogonal when changing one class does not impact another. When one class depends on another, change in the behaviour of the latter should not impact the former. Let’s assume that class A uses class B and its methods to do something. A change in class B should not break the existing contract between the two classes. The change should not change what class A expects from class B. If class B needs to be changed because another class C wants to use it and expects a slight different behaviour from class B. It's better to implement a separate method in B to serve C or create a subclass of B and use it in C.


Why do we need to have Orthogonality ?

The existing logic and behaviour of existing clients will break if we don't apply orthogonality. All the clients of the class where the change has taken place, will be impacted. A small change can explode many fold.


How to avoid writing non-Orthogonal code ?

I have already written about cohesion and coupling in another post. When there is less cohesion there is a chance that some code change will be non-Orthogonal. This is because, as related codes are scattered across different classes and methods they are interdependent on each other to complete one task. It is highly likely that change in one class will impact another. That is why we need to implement classes with high cohesion and make them as self contained as possible.


Sometimes classes depend on other classes because they delegate some part of their task which is not their core functionality. In this case we need to be careful that the contact/interface that has been used by the dependent class is not violated by the provider. That means we should not change the signature of the methods of the prover class. Means the return type, the types and number of the input. Also, the input values should not have extra restrictions that were not there before. The behaviour of the methods should not change either. For example, a method that was returning null as a value when it was not able to calculate using the given inputs should not be changed to throw exceptions. It may cause the client to break its logic.


When we need to introduce additional behaviour which is needed by another client it is recommended that we use method overloading or create a completely new method. This new method can reuse the existing logic of the old method and adds the extra logic needed. We can also extend a class and create child classes that will provide the new functionality or behaviours.


Example of non-Orthogonality

Let’s look at an example to clearly understand the above proposition.

public class Util {
  public int getLength(String str) {
    return str.length();
  }
}

public class EmployeeManager {

  private Util util;

  EmployeeManager(Util util) {
     this.util = util;
  }
  public Employee createEmployee( String fname, String lname, int age, String dept) {
    if (util.getLength(fname) == 0 && util.getLength(lname) == 0 
        && util.getLength(dept) == 0) {
      throw new Exception(“invalid input”);
    }
    return new Employee(fname, name, age, dept); 
  }
} 

Now if we change the Util class as below

public class Util {
  public int getLength(String str) throws Exception {
    if(str.length() == 0) {
      throw new Exception(“empty string”);
    }
    return str.length();
  }
}

Then the code in createEmployee() method in class EmployeeManager will fail as it does not have code to catch exceptions and act upon it. The signature of the getLength() method does not declare that it throws exception. The right approach should be to create another method named in Util class or create a subclass and implement the getLength() method that throws exceptions.


In this blog post today we have seen in detail the orthogonality principle and its importance and how to be compliant with this design principle.


44 views0 comments

Recent Posts

See All