Thursday, June 04, 2020

SOLID Design Principle Concept

Whereever we talk about software development it is mandate to follow certain rules so that we did not get into troble in later aspect of software development. Few of the aspect that we should think before developing any softare is
1- What should be the Architecture we should follow i.e. MVC, MVP, MVVP etc
2- Design Principle
3- Design Pattern :- Which can be classified further into Creational, Behavioural and Structural Design Pattern.
Here we will discuss about the Design Principle. The reason behind using this design principle it helps software to
- Maintainability
- Testability
- Flexibility and extensibility
- Parallel development
- Loose coupling

There are lot of design principle provided my Robert.C.Martin out of which few espically 5 Design principle taken as subset from them by Micael Feathers usig the acronym SOLID. Having said this lets ellaborate this SOLID Acronym
S :- Single Responsibility Principle (SRP)
O :- Open Close Principle (OCP)
L :- Liskov substitution Principle (LSP)
I :- Interface segregation Principle (ISP)
D :- Dependency Inversion Principle (DIP)
Now lets take one by one and understand them in depth
1- S :- Single Responsibility Principle (SRP)
As the name suggest according to this principle each class/interface should be responsible for one and one task/action/activity/. i.e. Each module/class should be responsible over single part of the functionality provided by the software.
Simple way to explain if you we have one login class/interface that will be responsible for loggin only and not for other activities
/**
*
*/
package Interface;
/**
* @author Siddhartha
*
*/
public interface IUser {
Boolean logIn(String userName, String password);
Boolean Registration(String userName, String password);

//notificaiton
boolean sendMail(String emailId);
boolean sendSMS(String phoneNumber);

//Log Error
void Error(String error);


}
As shown above the interface contains method not only for login and registration but also for notification and error which break SRP. As per SRP it should satisfy only one functionality of the software application in our case for login functionality we need only logIn and Registration method rest method we can divide into another interface as shown below i.e.

/**
*
*/
package Interface;
/**
* @author Siddhartha
*
*/
public interface IUser {
Boolean logIn(String userName, String password);
Boolean Registration(String userName, String password);

}
/**
*
*/
package Interface;
/**
* @author Siddhartha
*
*/
public interface INotification {
boolean sendMail(String emailId);
boolean sendSMS(String phoneNumber);

}
/**
*
*/
package Interface;
/**
* @author Siddhartha
*
*/
public interface IError {

void Error(String error);
}

2- I :- Interface segregation Principle (ISP)
As per this principle we should not have bulk Interface i.e. keeping all the method in one interface. In long run modifying this interface result into modifying all the class that implement it even tough if they are not using that methods. Best way is to divide Interface as per the functionaliy. Lets take below example
/**
* @author Siddhartha
*
*/
public interface IUser {
Boolean logIn(String userName, String password);
Boolean Registration(String userName, String password);

//notificaiton
boolean sendMail(String emailId);
boolean sendSMS(String phoneNumber);

//Log Error
void Error(String error);


}
As shown above as the interface contain all the method those class which implement this interface eventhough if they dont want notification of error functionality they have to override the method with empty implementation. Along with this in future if this interface changes then again all the class need to be override the new method in them respectively with even implementing into it which may result into testing of all the module. So best approcah is to divide the interface into small specific functionality interface as we did above i.e. divide IUser into IError and INotifications.
3- O :- Open Close Principle (OCP)
As per this priciple class/module.functions should always be open to be extended but should be closed to be modified at individual level. This means you can develop your class in such a way where other class can extends them and add additional functionality using he current one but should be changed them self to address the new requirement. Lets understand this using simple example.
/**
*
*/
package Classes;
/**
* @author Siddhartha
*
*/
public class OpenClosePrinciple {

public void getBonus(int salary)
{
int bonus = salary*10/100;
System.out.println("bonus:"+bonus);
//return bonus;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
OpenClosePrinciple objOpenClosePrinciple = new OpenClosePrinciple();
//For confirm employee

objOpenClosePrinciple.getBonus(1000);
}
}
OutPut:-
bonus:100
In above example we calculate bonus of the confirm employee. Everything working fine now lets say you have different type of employee i.e. confirm, contract, thirdparty and you want to canculate their bonus which is of different % i.e bonus for confirm employee is 10%, bonus for contract employee is 7% and bonus for third party employee is 5%. In this case there is two option either we modify the above class as given below
/**
*
*/
package Classes;
/**
* @author Siddhartha
*
*/
public class OpenClosePrinciple {

int bonus = 0;

public void getBonus(int salary, String employeeType)
{
if("Confirm".equals(employeeType))
{
bonus = salary*10/100;
System.out.println(" Confirm bonus:"+bonus);
}
else if("Contract".equals(employeeType))
{
bonus = salary*7/100;
System.out.println(" Contract bonus:"+bonus);
}
else
{
bonus = salary*5/100;
System.out.println("ThridParty bonus:"+bonus);
}

//return bonus;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
OpenClosePrinciple objOpenClosePrinciple = new OpenClosePrinciple();
//For confirm employee

objOpenClosePrinciple.getBonus(1000, "Confirm");
objOpenClosePrinciple.getBonus(1000, "Contract");
objOpenClosePrinciple.getBonus(1000, "ThridParty");
}
}
OutPut :-
Confirm bonus:100
Contract bonus:70
ThridParty bonus:50

Above example is good to go but as per te OCP priciple it is not good to modify the original class functionality. We already had use OpenClosePrinciple and to accomodate new requirement i.e. different type of emplyee we had modified the original class better option would be as give below
/**
*
*/
package Classes;
/**
* @author Siddhartha
*
*/
public abstract class OpenClosePrinciple {


public abstract void getBonus(int salary);

/**
* @param args
*/
/*
* public static void main(String[] args) { // TODO Auto-generated method stub
* OpenClosePrinciple objOpenClosePrinciple = new OpenClosePrinciple(); //For
* confirm employee
*
* objOpenClosePrinciple.getBonus(1000, "Confirm");
* objOpenClosePrinciple.getBonus(1000, "Contract");
* objOpenClosePrinciple.getBonus(1000, "ThridParty");
*
*
* }
*/
}

/**
*
*/
package Classes;
/**
* @author Siddhartha
*
*/
public class ConfirmEmployee extends OpenClosePrinciple {
@Override
public void getBonus(int salary) {
// TODO Auto-generated method stub
int bonus = salary*10/100;
System.out.println(" Confirm bonus:"+bonus);

}
}
/**
*
*/
package Classes;
/**
* @author Siddhartha
*
*/
public class ContractEmployee extends OpenClosePrinciple {
@Override
public void getBonus(int salary) {
// TODO Auto-generated method stub
int bonus = salary*7/100;
System.out.println(" Contract bonus:"+bonus);

}
}
/**
*
*/
package Classes;
/**
* @author Siddhartha
*
*/
public class MainEmployeeClass {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
OpenClosePrinciple objConfirmEmployee = new ConfirmEmployee();
objConfirmEmployee.getBonus(1000);

OpenClosePrinciple objContractEmployee = new ContractEmployee();
objContractEmployee.getBonus(1000);
}
}
OutPut:-
Confirm bonus:100
Contract bonus:70
As in above example we have made the getBonus method as abstract and allowed the different specific class to extend this class overide this method for their specific use we make sure that our abstract class will need not be changed in the future for specific type of employee bonus.
4- L :- Liskov substitution Principle (LSP)
As per this priciple when ever we child class extend parent class in that case Parent class object can be replaced by child class. Little bit confused...
If S is the subtype of Parent class P, then objects of type P may be replaced by the object of Type S. But the catch here is this rule it also state that the new derived class should not produce any exception that is not in the parent class.
In our above example we had implemented partially this principle i.e.
/**
* @author Siddhartha
*
*/
public class MainEmployeeClass {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
OpenClosePrinciple objConfirmEmployee = new ConfirmEmployee();
objConfirmEmployee.getBonus(1000);

OpenClosePrinciple objContractEmployee = new ContractEmployee();
objContractEmployee.getBonus(1000);
}
}
here we have created Object of Child class ConfirmEmployee and ContractEmployee. Also we had replaced completely the original parent class object i.e. OpenClosePrinciple. Lets say we have another Employee type as thirdparty and they are not eligible for bonus. If using above concept if implement the same logic in that case we need to throw an exception as this thridparty employee is not eligible for bonus and this breaks our LSP principle where in derived class should not throw an exception that is not present in the base/parent class.
/**
*
*/
package Classes;
/**
* @author Siddhartha
*
*/
public class ThirdPartyEmployee extends OpenClosePrinciple {
@Override
public void getBonus(int salary) {
// TODO Auto-generated method stub
try {
throw new Exception("Not eligible for bonus");
}
catch(Exception e)
{
e.printStackTrace();
}

}
}
/**
*
*/
package Classes;
/**
* @author Siddhartha
*
*/
public class MainEmployeeClass {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
OpenClosePrinciple objConfirmEmployee = new ConfirmEmployee();
objConfirmEmployee.getBonus(1000);

OpenClosePrinciple objContractEmployee = new ContractEmployee();
objContractEmployee.getBonus(1000);

OpenClosePrinciple objThirdPartyEmployee = new ThirdPartyEmployee();
objThirdPartyEmployee.getBonus(1000);

}
}
OutPut:-
Confirm bonus:100
Contract bonus:70
java.lang.Exception: Not eligible for bonus
at Classes.ThirdPartyEmployee.getBonus(ThirdPartyEmployee.java:16)
at Classes.MainEmployeeClass.main(MainEmployeeClass.java:24)
To over come this best option is to use Interface and use contract as required

/**
*
*/
package Classes;
/**
* @author Siddhartha
*
*/
public abstract class OpenClosePrinciple {


public abstract int getSalary(int salary);

/**
* @param args
*/
/*
* public static void main(String[] args) { // TODO Auto-generated method stub
* OpenClosePrinciple objOpenClosePrinciple = new OpenClosePrinciple(); //For
* confirm employee
*
* objOpenClosePrinciple.getBonus(1000, "Confirm");
* objOpenClosePrinciple.getBonus(1000, "Contract");
* objOpenClosePrinciple.getBonus(1000, "ThridParty");
*
*
* }
*/
}
package Interface;
/**
* @author Siddhartha
*
*/
public interface IEmployeeBonus {
public void getBonusInterface(int salary) ;
}
/**
*
*/
package Classes;
import Interface.IEmployeeBonus;
/**
* @author Siddhartha
*
*/
public class ConfirmEmployee extends OpenClosePrinciple implements IEmployeeBonus{
@Override
public void getBonusInterface(int salary) {
// TODO Auto-generated method stub
int bonus = salary*10/100;
System.out.println(" Confirm bonus:"+bonus);

}
@Override
public int getSalary(int salary) {
// TODO Auto-generated method stub
return salary;
}
}

/**
*
*/
package Classes;
import Interface.IEmployeeBonus;
/**
* @author Siddhartha
*
*/
public class ContractEmployee extends OpenClosePrinciple implements IEmployeeBonus{

@Override
public void getBonusInterface(int salary) {
// TODO Auto-generated method stub
int bonus = salary*7/100;
System.out.println(" Contract bonus:"+bonus);
}
@Override
public int getSalary(int salary) {
// TODO Auto-generated method stub
return salary;
}
}
/**
*
*/
package Classes;
import Interface.IEmployeeBonus;
/**
* @author Siddhartha
*
*/
public class ThirdPartyEmployee extends OpenClosePrinciple implements IEmployeeBonus{

@Override
public void getBonusInterface(int salary) {
// TODO Auto-generated method stub
System.out.println("No bonus for TirdParty Employee");

}
@Override
public int getSalary(int salary) {
// TODO Auto-generated method stub
return salary;
}
}

/**
*
*/
package Classes;
import Interface.IEmployeeBonus;
/**
* @author Siddhartha
*
*/
public class MainEmployeeClass {
/**
* @param args
*/
public static void main(String[] args) {

// To get salary we can have contract as given below

// TODO Auto-generated method stub
OpenClosePrinciple objConfirmEmployee = new ConfirmEmployee();
int ConfirmEmployeeSalary = objConfirmEmployee.getSalary(1000);
System.out.println("ConfirmEmployeeSalary:"+ConfirmEmployeeSalary);


OpenClosePrinciple objContractEmployee = new ContractEmployee();
int ContractEmployeeSalary = objContractEmployee.getSalary(1000);
System.out.println("ContractEmployeeSalary:"+ContractEmployeeSalary);


OpenClosePrinciple objThirdPartyEmployee = new ThirdPartyEmployee();
int ThridPartyEmployeeSalary = objThirdPartyEmployee.getSalary(1000);
System.out.println("ThridPartyEmployeeSalary:"+ThridPartyEmployeeSalary);

//here if we try to access the bonus we will get the exception i.e.
//objConfirmEmployee.getBonusInterface(ConfirmEmployeeSalary);


//To get the bonus use contract of interface
IEmployeeBonus objIEmployeeBonusConfirmEmployee = new ConfirmEmployee();
objIEmployeeBonusConfirmEmployee.getBonusInterface(ConfirmEmployeeSalary);


IEmployeeBonus objIEmployeeBonusContractEmployee = new ContractEmployee();
objIEmployeeBonusContractEmployee.getBonusInterface(ConfirmEmployeeSalary);

IEmployeeBonus objIEmployeeBonusThirdPartyEmployee = new ThirdPartyEmployee();
objIEmployeeBonusThirdPartyEmployee.getBonusInterface(ConfirmEmployeeSalary);
}
}

OutPut:-
ConfirmEmployeeSalary:1000
ContractEmployeeSalary:1000
ThridPartyEmployeeSalary:1000
Confirm bonus:100
Contract bonus:70
No bonus for TirdParty Employee

5- D :- Dependency Inversion Principle (DIP)
As per this priciple higher modules shoule not depends on low-level modules. Both should depends on abstraction. This means interaction of higher module with the lower module shoule be abstract. Best example is in server side we use to make call to DAOIMPL from BO class but this is not good design .
i.e. instead of this
BO--> DAOImpl -->SRVImpl
Use this :- Business object to Data Access Object interface and from Data Access Object interface to its Data Access Object Implementations.
BO--> DAO--> DAOImpl -->SRV --> SRVImpl

No comments: