SOLID Principles

In this post, I am going to discuss SOLID principles, yes I mean the holy grail of OOP that empower developers to produce better software. 

Let's start with the definition. SOLID Principles is an acronym for five design principles intended to make software design more understandable, flexible and  maintainable; including Single Responsibility principle, Open-Closed principle, Liskov Substitution principle, Interface Segregation principle and Dependency Inversion principle.

The consequence of following SOLID principles will lead developers towards more extendable, logical and easier to read software; and even thought it seems like an irony to have a SOLID software but we all hope to these principles guide us toward more understandable, flexible and maintainable software. Now let's get into the principles' details.

Single Responsibility Principle (SRP)
A class should have only a single responsibility, that is, only changes to one part of the software's specification should be able to affect the specification of the class. 
In other words, Software module should have one and only one reason to change. Now the question is: What defines a reason to change?
According to Robert C. Martin, It is people who request changes and hence the reasons for change are people. you don’t want to confuse those people, or yourself, by mixing together the code that many different people care about for different reasons.
SRP increases the cohesion between things that change for the same reasons, and  decrease the coupling between those things that change for different reasons.

The following code snippet is an example of breaking SRP, since CustomerOrderService class is responsible for calculating, saving and also sending order email. 

Open-Closed Principle(OCP)
A module should be open for extension, but closed for modification. 
OCP is achieved through Abstractions and Polymorphism. A simple way to apply OCP is to implement the new functionality on new derived (sub)classes that inherit the original class implementation. Another way is to mediate client access to the original class with an abstract interface, so new functionality can be implemented on new classes that are accessed through the same interface.

The following snippet demonstrates an example Employee class that violates OCP principle as well as an approach to avoid the violation.

Liskov Substitution Principle(LSP)
Subclasses should be substitutable for their base classes.
LSP is a way of ensuring that inheritance is used correctly such that inheritance instead of "IS-A" relationship is a "IS-SUBSTITUTABLE-FOR" relationship.

Liskov's principle imposes some standard requirements on signatures:
  • Contravariance of method arguments in the subtype.
  • Covariance of return types in the subtype. 
  • No new exceptions should be thrown by methods of the subtype, except where those exceptions are themselves subtypes of exceptions thrown by the methods of the supertype.
In addition to the signature requirements, the subtype must meet a number of behavioral conditions (design by contract methodology):
  • Preconditions cannot be strengthened in a subtype.
  • Postconditions cannot be weakened in a subtype.
  • Invariants of the supertype must be preserved in a subtype.
  • History constraint (the "history rule"). A violation of this constraint can be exemplified by defining a mutable point as a subtype of an immutable point. In the history of the immutable point, the state is always the same after creation, so it cannot include the history of a mutable point in general.
The following code shows an example of LSP violation named Square/Rectangle dilemmas.

Interface Segregation Principle(ISP)
Many client-specific interfaces are better than one general-purpose interface.
ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them. In another word, correct abstraction is the key to Interface Segregation Principle. The following code snippet shows an example of ISP principle violation and a solution to avoid the violation.

Dependency Inversion Principle (DIP)
Depend upon abstractions, do not depend on concretions. 
  • High level modules should not depend upon low level modules. Both should depend upon abstractions.
  • Abstractions should not depend upon details. Details should depend upon abstractions.
The following code snippet demonstrates a DIP violation example since CustomerController class depends on CustomerService and DbLogger concrete classes; and a solution to avoid the violation.


Popular Posts