SOLID: 5 Object Oriented Design Principle for Software Development

There are 5 principles that any software developer should take into account while programming. One of the biggest advantage, if we follow these principles, in my opinion is that there will be reduced code smells. Here are the principles:

Single Responsibility Principle (S)

This principle tells us that a class should have a particular objective and it should not be created to serve more than 1 objective.

Bad Example:

public class Employee {
     private String empId;
   
     public void generateAnnualReport() {
       // .. generates the report
     }
     
     public void addNewEmployees(Employee e) {  
         // .. adds a new employee
     }
 }

The class above is not having a single responsibility but rather class has more than 1 purpose. Hence, according to the Single Responsibility principle

Good Example:

 public class Employee {
   
   private String empId;
   // getter and setters
 }

public class EmployeeReport {
   // class for generating the employee annual report
}

public class AddNewEmployee {
   // class for adding new employee
}

Open Closed Principle (0)

This principle means that software code (classes, methods, modules etc) should be open for extension but closed for modification.

√ This mean new behaviour can be added in order to meet the new requirements.

 ×This does not mean that new behaviour can be added by modifying the existing code.

Continuing with the previous example, Imagine there are several types of Employee Annual Reports as shown in the example below.

Bad Example:

 public class TravelExpenseReport {}
 public class MealExpenseReport {}

 public class ExpenseReport {
    
   public void getExpenseReport(Object typeOfReport) {
      if (typeOfReport instanceOf TravelExpenseReport) {
         // call the expense method of Travel Expense Report
      } else if (typeOfReport instanceOf MealExpenseReport) { 
 // call the expense method of Meal Expense Report }
 else { 
 // not supported exception
 }
}

 

The above example is bad because imagine for any new report addition, the if else structure will need to be modified thus Open Close Principle is violated.

Good Example:

public abstract class ExpenseBase {
     abstract void expense();
} 

public class TravelExpenseReport extends ExpenseBase {

     @Override
     public void expense() {
        // define the method
     }
}

public class MealExpenseReport extends ExpenseBase {
  
      @Override
 public void expense() { 
 // define the method
 }
}

public class ExpenseReport { 
 public void getExpenseReport(ExpenseBase expenseObj) {
 expenseObj.expense();
 }
 }

The above example is good because an addition of another expense report will not modify any of these classes itself but rather it would be an extension (as another class would be added).

Liskov Substitution Principle (L)

It states objects in a program should be replaceable with instances of their subtypes without altering the correctness of the program.  In other words, it states derived classes should extend the base classes without changing their behaviour. It is an extension of Open Closed principle.

Interface Segregation Principle (I)

It states that a class should not implement an interface if its not intended for its use. If not, a class might have methods that it is forced to implement but were not needed.

Bad Design:

public interface class ExpenseBase {
     void expense();
     void expenseMonthly();
     void expenseMealQuarterly();
} 

public class TravelExpenseReport implements ExpenseBase {

     @Override
     public void expense() {
       // define method
     }

     @Override
     public void expenseMonthly() {
      // define method
     }

     @Override
     public void expenseMealQuarterly() {
      // Do nothing as this method is not intended for class.
      // This class has been forced to implement this method
      }

}

public class MealExpenseReport implements ExpenseBase {

      @Override
 public void expense() { 
 // define method
 }
 
 @Override
 public void expenseMonthly() {
 // define method
 }

 @Override
 public void expenseMealQuarterly() {
 // define method
 }
 }

The above example is bad because Travel Expense Report class is forced to implement the method expenseMealQuarterly() even though its intended for Meal Expense Report class.

Good Design:

public interface class ExpenseBase {
     void expense();
     void expenseMonthly();
} 

public interface class ExpenseMealBase {

     void expenseMealQuarterly();
}

public class TravelExpenseReport implements ExpenseBase {

     @Override
     public void expense() {
       // define method
     }

     @Override
     public void expenseMonthly() {
      // define method
     }
}

public class MealExpenseReport implements ExpenseBase, ExpenseMealBase {

       @Override
 public void expense() { 
 // define method
 } 
 @Override
 public void expenseMonthly() { 
 // define method
 }
 @Override
 public void expenseMealQuarterly() { 
 // define method
 }
 }

The above example is good because interfaces have been segregated now and the class which does not need a specific interface will not implement and hence will not be forced to implement any method that it does not need it.

Dependency Inversion Principle (D)

This principle states that High Level modules should not depend on low level modules. Both of them should depend on abstractions. This mean a particular class should not depend on directly on another class but rather on an abstraction (or interface) of this class.

BAD DESIGN:

public class Employee {
   private EmployeeTeam empTeam;

   public String getEmpData(EmployeeTeam empTeam) {
      //... 
      return empTeam;
   }  
   //.....
}

This is a bad design because Employee and EmployeeTeam are two individual modules. Employee class (High Level) depends on EmployeeTeam (Low Level) class directly for the operations and hence it violates the principle of Dependency Inversion.

GOOD DESIGN:

public inteface EmpData {
    String getEmpData();
     //...
} 
public class EmployeeTeam implements EmpData {
 
   @Override
   public String getEmpData() {
   //...
   return ..
   }
}
public class Employee {
 private EmployeeTeam empTeam;

 public String getData(EmpData empData) {
    // .. return the data
 } 
 //.....
}

This design is considered good because we do not couple the classes together. EmployeeTeam class implements an interface which has a method getEmpData. Employee class will use this interface to call the EmployeeTeam method thus both High Level (Employee) and low level (EmployeeTeam) depend on abstraction now.


Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s