S.O.L.I.D. Design Principles.


Design Principle and Patterns
Design Principle and Patterns

S.O.L.I.D. Design principles suggest that the Individual pieces / building blocks of software should be of solid quality and highly accurate in design. For e.g. build blocks of rockets or Formula 1 car. The high quality software should follow principles of SOLID design principles by Martin R Fowler. The solid principles depend on the following principles

1. Agile Software Development

2. You Aren’t Gonna Need it

3. Keep it simple stupid

4. Vertical slice

5. Big Ball of mud.

Design Pattern is solution to common problems encountered at the software design level where reusability of code is very easy and flexible. Learning of Design Pattern and applying them in production environment needs lots of practice of developing software and experiencing the software programming. So many beginners overuse design patterns or use wrong design pattern.

The object oriented design implementation should be mostly composed of interfaces and abstract classes between two concrete implementations, this helps in loose coupling of dependency and software is less rigid for change in software requirements. Otherwise it will be similar to removing the windscreen when we need to change the steering wheel of the car.

Hence interfaces based programming is preferred over concrete classes based programming for a high quality development of the software. The five standard Design principles are as follows

  1. (S) The Single Responsibility Principle.
  2. (O) The Open/Closed Principle.
  3. (L) The Liskov Substitution Principle.
  4. (I) The Interface Segregation Principle
  5. (D) The Dependency-Inversion Principle.

I have briefly explained the 5 standard S.O.L.I.D. design principles below

The Single Responsibility Principle

A class should have only one reason to change. In other words Single Responsibility principle is defined as “THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE.”

public interface Socket

{

public void Connect(string pno);

public void Disconnect();

public void SendData(char c);

public char RecvData ();

}

Single Responsibility Principle applied to Socket Interface to have separated responsibilities, One responsibility is Connection to the port and Disconnection to the port. Second responsibility is communication of Data with the server port.




The Open/Closed Principle (OCP)

Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.

Modules that conform to the open-closed principle have two primary attributes.

They are “Open For Extension”.

They are “Closed for Modification”.

In most of the design scenarios the Open Closed principle is at the core of object oriented design. Adherence to this principle is what yields the greatest benefits of object oriented methodology; i.e. reusability and maintainability.

Here the Render Interface is both open and closed interface, as Render Implementation uses different implementation of render depending on the device on which page is being displayed. So a Render instance uses RenderPage Interface which is closed w. r. t. adding new methods or changing the render methods but it is open to using the derivative of Display device instances.

Extension methods and static class are examples of violations of SOLID principles. All third party code should be encapsulated and isolated from developer code, so third party code can be later replaced by other pieces of code as new versions of code are generated. Method parameters should be interface type not specific class type.

There are two types of Open-Closed Principle Implementation, namely

Meyers open closed principle: Here developer is allowed to modify the class design only to fix the bugs and developers are prohibited from changing the behavior or state of the class.

Polymorphic open closed principle: Apart from maintaining the behavior and state of the class developer should make all the variables private and avoid the Global variables.

The Liskov Substitution Principle

Subtypes must be substitutable for their base types when inject into the functions.

In other words we can define Liskov Substitution Principle as follows:

FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT.


The Liskov Substitution Principle is one of the prime enablers of OCP. The substitutability of subtypes allows a module, expressed in terms of a base type, to be extensible without modification. That substitutability must be something that developers can depend on implicitly. Thus, the contract of the base type has to be well and prominently understood, if not explicitly enforced, by the code.

The term IS-A is too broad to act as a definition of a subtype. The true definition of a subtype is substitutable, where substitutability is defined by either an explicit or implicit contract. Functions that use pointers and reference base classes as parameters, they are substituted by subclass instances without knowledge of the functions that will process them. This substitution is applied through Polymorphism usually by developer who is following Test Driven Design methodologies; One care must be that code implementing Liskov Substitution Principle should avoid Run Time Type Information.

E.g.1. Inheritance of Line and LineSegment is the case of LSP.

In the case of the Line and LineSegment, a simple solution illustrates an important tool of OOD. If we have access to both the Line and LineSegment classes, we can factor the common elements of both into an abstract base class LinearObject.

E.g.2. Peel method for Oranges is different than Peel method for apples. But Peel method is common and can be used as an interface method.

E.g.3. Similarly width method is common for Square and Rectangle.

The Dependency-Inversion Principle

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Abstractions should not depend upon details. Details should depend upon abstractions.

In the example of remote controlling the Television, the internal working of either remote or TV is not known to each other but each interact.


Remote instance will interact with the Universal TV interface and TV instance interacts with Universal Remote interface. So both remote models and TV models can be changed so there is dependency between them.

Consider the software that might control the regulator of a furnace. The software can read the current temperature from an I/O channel and instruct the furnace to turn on or off by sending commands to a different I/O channel. The structure of the algorithm might look something like

const byte TERMOMETER = 0x86; 
const byte FURNACE = 0x87; 
const byte ENGAGE = 1; 
const byte DISENGAGE = 0; 
void Regulate(double minTemp, double maxTemp) 
{ 
 for(;;) 
 { 
 while (in(THERMOMETER) > minTemp) 
 wait(1); 
 out(FURNACE,ENGAGE); 
 while (in(THERMOMETER) < maxTemp) 
 wait(1); 
 out(FURNACE,DISENGAGE); 
 } 
} 

void Regulate(Thermometer t, Heater h, 
 double minTemp, double maxTemp) 
{ 
 for(;;) 
 { 
 while (t.Read() > minTemp) 
 wait(1); 
 h.Engage(); 
 while (t.Read() < maxTemp) 
 wait(1); 
 h.Disengage(); 
 } 
} 

This shows that the Regulate function takes two arguments that are both interfaces. The Thermometer interface can be read, and the Heater interface can be engaged and disengaged. This is all the Regulate algorithm needs. Now it can be written as shown above.

This has inverted the dependencies such that the high-level regulation policy does not depend on any of the specific details of the thermometer or the furnace. The algorithm is nicely reusable. Passing a reference of Interface and instantiation happens inside a static class.

The Interface Segregation Principle

Clients should not be forced to depend on methods they do not use. In other words Integration Segregation Principle is defined as follows:

CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACESTHAT THEY DO NOT USE.

For e.g.

Public interface UserLogin {

Public string Username;

Public string Password

Public string role;

Public string authorization;

}

So for user to login you would require only a username and password and nothing more. So the above interface needs to be broken into related interfaces and then used as two instances for different responsibilities

Public interface userLogin {

Public string getusername();

Public string getpassword();

}

Public interface Authorization {

Public string role;

Public string authorization;

Public string group;

}

Public class user implements userLogin {

String Username {get; set ;}

String Password {get; set ;}

}

Public class Groups implements Authorization, UserLogin {

Public Username {get; set; }

Public string role {get; set; }

Public string authorization {get; set;}

Public string group {get; set;}

}


Fat classes cause bizarre and harmful couplings between their clients. When one client forces a change on the fat class, all the other clients are affected. Thus, clients should have to depend only on methods that they call. This can be achieved by breaking the interface of the fat class into many client-specific interfaces. Each client-specific interface declares only those functions that its particular client or client group invoke. This breaks the dependence of the clients on methods that they don’t invoke and allows the clients to be independent of one another.

The ISP acknowledges that objects require non-cohesive interfaces; but it is advised that user of these objects should not know about them as a single class. Instead, users of these objects should know about abstract base classes that have cohesive interfaces; and are multiply inherited into the concrete class that describes the non-cohesive object.

This chapter has introduced the concept of design principle used in object oriented design and architecture of application. and I have try to explain very briefly the design principle applied to the structure of classes and interfaces helps in keeping the software application flexible, robust, reusable, and developable.

In my next series of blogs I would try to explain briefly the twenty three official standard design patterns used in Object oriented design. Also provide me the comments feedback of this blog so that I can improve my future articles.

Leave a comment