Detail Description of OOAD Principles
DRY (Don't repeat yourself):
The first object oriented design principle is DRY, as name suggest DRY (don't repeat yourself) means don't write duplicate code, instead use Abstraction to abstract common things in one place. If you have block of code in more than two place consider making it a separate method, or if you use a hard-coded value more than one time make them public final constant. Benefit of this Object oriented design principle is in maintenance. It's important not to abuse it, duplication is not for code, but for functionality . It means, if you used common code to validate OrderID and SSN it doesn’t mean they are same or they will remain same in future. By using common code for two different functionality or thing you closely couple them forever and when your OrderID changes its format , your SSN validation code will break. So beware of such coupling and just don’t combine anything which uses similar code but are not related.
Encapsulate What Changes:
Only one thing is constant in software field and that is "Change", So encapsulate the code you expect or suspect to be changed in future. Benefit of this OOPS Design principle is that Its easy to test and maintain proper encapsulated code. If you are coding in Java then follow principle of making variable and methods private by default and increasing access step by step e.g. from private to protected and not public. Several of design pattern in Java uses Encapsulation, Factory design pattern is one example of Encapsulation which encapsulate object creation code and provides flexibility to introduce new product later with no impact on existing code.
Favor Composition over Inheritance:
Always favor composition over inheritance, if possible. Some of you may argue this, but I found that Composition is lot more flexible than Inheritance. Composition allows to change behavior of a class at runtime by setting property during runtime and by using Interfaces to compose a class we use polymorphism which provides flexibility of to replace with better implementation any time. Even Effective Java advise to favor composition over inheritance.
Don't do all stuff by yourself, delegate it to respective class. Classical example of delegation design principle is equals() and hashCode() method in Java. In order to compare two object for equality we ask class itself to do comparison instead of Client class doing that check. Benefit of this design principle is no duplication of code and pretty easy to modify behavior.
Single Responsibility Principle (SRP):
"Whenever a software system must support a set of alternatives, ideally only one class in the system knows the entire set of alternatives".
Single Responsibility Principle is another SOLID design principle, and represent "S" on SOLID acronym. As per SRP, there should not be more than one reason for a class to change, or a class should always handle single functionality. If you put more than one functionality in one Class in Java it introduce coupling between two functionality and even if you change one functionality there is chance you broke coupled functionality, which require another round of testing to avoid any surprise on production environment.
Open Closed Design Principle:
The Open-Closed Principle (OCP) says that we should attempt to design modules that never need to be changed. According to OCP rules classes, functions and modules should meet these two criteria
- Open For Extension - The behavior of the module or class can be extended to meet new requirements.
- Closed For Modification - The source code of the module is not allowed to change.
In general Classes, methods or functions should be Open for extension (new functionality can be added to them) and Closed for modification.
This design principle prevents some-one from changing already tried and tested code. Ideally if you are adding new functionality only than your code should be tested and that's the goal of Open Closed Design principle. Most of the time it is not possible to have all the modules of a software system satisfy the OCP, but we should attempt to minimize the number of modules that do not satisfy it.
The Open-Closed Principle is really the heart of OO design, and comes under SOLID design principle and OCP represents "O" from 'SOLID' acronym
Conformance to this principle yields the greatest level of reusability and maintainability.
Liskov Substitution Principle (LSP):
"Functions That Use References to Base (Super) Classes must be able to use Objects Of Derived (Sub) Classes without knowing it"
According to Liskov Substitution Principle, Subtypes must be substitutable for super type i.e. methods or functions which uses super class type must be able to work with object of sub class without any issue". LSP is closely related to Single responsibility principle and Interface Segregation Principle. If a class has more functionality than subclass might not support some of the functionality ,and does violated LSP. In order to follow LSP SOLID design principle, derived class or sub class must enhance functionality, but not reduce them. LSP represent "L" on SOLID acronym.
But we must be careful when we implement subclasses to insure that we do not unintentionally violate the LSP. If a function does not satisfy the LSP, then it probably makes explicit reference to some or all of the subclasses of its superclass. Such a function also violates the Open-Closed Principle, since it may have to be modified whenever a new subclass is created.
- The LSP makes it clear that the ISA relationship is all about behavior
- In order for the LSP to hold (and with it the Open-Closed Principle) all subclasses must conform to the behavior that clients expect of the base classes they use
- The guarantee of the LSP is that a subclass can always be used wherever its base class is used!
Interface Segregation Principle stats that, a client should not implement an interface, if it doesn't use that. This happens mostly when one interface contains more than one functionality, and client only need one functionality and not other.Interface design is tricky job because once you release your interface you can not change it without breaking all implementation. Another benefit of this design principle in Java is, interface has disadvantage to implement all method before any class can use it so having single functionality means less method to implement. ISP represent "I" on SOLID acronym.
Programming for Interface not implementation:
Always program for interface and not for implementation this will lead to flexible code which can work with any new implementation of interface. So use interface type on variables, return types of method or argument type of methods in Java. This has been advised by many Java programmer including in Effective Java and head first design pattern book.
Dependency Injection or Inversion principle:
In object-oriented programming, the dependency inversion principle refers to a specific form of decoupling where conventional dependency relationships established from high-level modules to low-level modules, dependency modules are inverted (i.e. reversed) for the purpose of rendering high-level modules independent of the low-level modules implementation details. This SOLID design principle represent "D" on SOLID acronym.
Dependency Injection has been very well implemented in Spring framework, beauty of this design principle is that any class which is injected by DI is easy to test with mock object and easier to maintain because object creation code is centralized in framework and client code is not littered with that. There are multiple ways to implemented Dependency injection.
Applying the dependency inversion principle can also be seen as applying the Adapter pattern, i.e. the high-level class defines its own adapter interface which is the abstraction that the high-level class depends on. The adaptee implementation also depends on the adapter interface abstraction (of course, since it implements its interface) while it can be implemented by using code from within its own low-level module. The high-level has no dependency to the low-level module since it only uses the low-level indirectly through the adapter interface by invoking polymorphic methods to the interface which are implemented by the adaptee and its low-level module.
All these object oriented design principle helps you write flexible and better code by striving high cohesion and low coupling. Theory is first step, but what is most important is to develop ability to find out when to apply these design principle. Find out, whether we are violating any design principle and compromising flexibility of code, but again as nothing is perfect in this world, don't always try to solve problem with design patterns and design principle they are mostly for large enterprise project which has longer maintenance cycle.