Clean Code and Design Principles Complete Guide
- Introduction to Clean Code and Software Design Principles
- Writing Meaningful Variable Names
- Designing Good Functions and Classes
- Software Design Principles - I (DRY, YAGNI, KISS, etc)
- Software Design Principles - II (Abstraction, Extensibility, Cohesion)
- Software Design Principles - III (SOLID Principles)
SOLID Design Principles are a set of five software design principles promoted by Robert C. Martin (Uncle Bob) in his paper "Design Principles and Design Patterns".
The SOLID principles aim to make software design easy to understand, maintain, and, flexible.
The SOLID Design Principles are:
- Single-responsibility principle: "There should never be more than one reason for a class to change." In other words, every class should have only one responsibility.
- Open-Closed principle: "Software entities ... should be open for extension, but closed for modification."
- Liskov substitution principle: "Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it".
- Interface segregation principle: "Many client-specific interfaces are better than one general-purpose interface."
- Dependency inversion principle: "Depend upon abstractions, [not] concretions."
Before we get into understanding each of the five principles in-depth, let's note these three points:
- Make sure that classes interact with each other through interfaces.
- Create small classes that do a single thing.
- Create small interfaces such that a class only has to implement functionalities that it should.
The above three points if followed well will also result in following the SOLID principles.
Single Responsibility Principle (SRP)
The Single Responsibility Principle states that there should never be more than one reason for a class to change. In other words, every class should have only one responsibility.
If there are two reasons to change a class then it has more than one responsibility and should be split. Having multiple reasons to change means that the module has high coupling.
The more responsibilities your class has, the more often you'll need to change it. Since they won't be independent of each other, change to one responsibility may affect the other.
Open-Closed Principle (OCP)
The Open-Closed Principle states that a software entity (class, module, function) should be open for extension but closed for modification. We should write our modules so that they can be extended, without requiring them to be modified.
Open for extension: A class should be extensible, i.e., we should be able to make a class behave in different ways as new requirements come.
Closed for modification: The extension should happen without any modification to the class.
This can be achieved through abstraction, i.e., by creating interfaces. Interfaces should not change based on new requirements. Interfaces are extensible and new requirements can be taken care of by implementing classes.
Liskov Substitution Principle (LSP)
The Liskov Substitution Principle states that subclasses should be substitutable for their base classes. Derived classes should be substitutable for their base classes wherever the derived class is used.
A client using a base class should continue to function properly even if a derived class is passed to it. This means that we should be able to pass an object of class B to any function that expects an object of class A given that class B is a subclass of class A.
Dependency Inversion Principle (DIP)
The Dependency Inversion Principle states that "Depend upon abstractions, [not] concretions". This means that classes should depend on abstractions (interfaces) of other classes instead of the concrete implementations.
The dependencies are inverted as the concrete classes depend upon abstractions instead of being dependent upon by other classes.
Interface Segregation Principle (ISP)
The Interface Segregation Principle states that many client-specific interfaces are better than one general-purpose interface.
Clients should not be forced to depend upon interfaces that they do not use. It is better to have smaller interfaces than fewer, fatter interfaces. An interface should be dependent more on the code that calls it rather than the code that implements it.
Therefore, a class should be implementing different interfaces created based on the different types of clients. Each of these interfaces might have common methods as well.
References: Design Principles and Design Patterns






