SOLID Principles: Basic Building Blocks of (A Clean) Software System
This article introduces and provides examples for each of the five SOLID principles to build a clean code architecture.
Join the DZone community and get the full member experience.Join For Free
Good software system begins with clean code and by "clean code," I mean it is code that is easy to understand and easy to change. SOLID principles helps us write clean code, as the code is like humor: when you have to explain it, it’s bad. The SOLID principles tell us how to arrange our methods/functions and how those classes should be interrelated to each other and the data structures into classes.
The goal of the principles is the creation of software structures that is easy to modify and easy to understand. This blog provides an introduction to the SOLID principles.
The SOLID Principles are as follows:
S – Single Responsibility principle
O – Open-Closed principle
L – Liskov Substitution principle
I – Interface Segregation principle
D – Dependency Inversion principle
Single Responsibility Principle (SRP)
According to Rober C. Martin, the Single Responsibility principle means "a class or modules should have one, and only one, reason to change," or we can say, "There should be no more than one reason to modify a class or a module."
Example: An EmployeeReviewer class is only responsible for counting the appraisal score of an employee according to his/her performance criteria fixed by the company. This class should only change if the criteria fixed by the company will change.
Open-Closed Principle (OCP)
The OCP states that, "A software artifact should be open for extension, but closed for modification." It means that a software structure should be designed so that, with every new release we be adding code for new functionality instead of modifying the previous one. In other words, the behavior of a software artifact ought to be extendable, without having to modify that artifact.
Example: Software development is similar to building a multi-story building. So the design should be like that if we are going to add a new feature then this can be easily achieved by adding some new functions/methods or classes instead of modifying the previous one. If we are planning to make the house that already has a room in it, then to make a kitchen, if we have to modify the already created rooms for it, then obviously the design for building the house is not correct. The same rules apply for designing the software system.
Liskov Substitution Principle (LSP)
LSP tells us that "Derived classes must be substitutable for their base classes." What exactly Barbara Liskov wrote "What is wanted here is something like the following substitution property: If for each object O1 of type S there is an object O2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when O1 is substituted for O2 then S is a subtype of T." In very easy words, we can also say that "objects in a program should be replaceable with instances of their subtypes without altering the correctness of the program."
Example: You have an instance of the class Bike which our program uses to perform a drive action. This instance could be replaced by an instance of the class Honda, if Honda is a subclass of Bike.
Interface Segregation Principle (ISP)
ISP states, "Many client-specific interfaces are better than one general purpose interface." The heart of the principle is quite simple. If you have a class that has several clients, rather than loading the class with all the methods that the clients need, create specific interfaces for each client and multiply them into the class.
Example: Suppose we have a company management system with different type of roles like admin, HR, employee, etc. Instead of a class which can perform all the actions for all these types, we would create separate classes for each type which implement their specific actions according to our use case.
Dependency Inversion Principle (DIP)
DIP states, "Depend upon abstractions. Do not depend upon concretions." The DIP states the primary mechanism. Dependency Inversion is the strategy of depending upon interfaces or abstract functions and classes, rather than upon concrete functions and classes. This structure starts at the top and points down towards details. High-level modules depend upon lower level modules, which depend upon yet lower level modules.
Example: Imagine we have a class distributor which is able to share a blog post on different platforms like Twitter and Facebook. According to the Interface Segregation Principle, the distributor uses a composition of several instances, like a TwitterShareAction and a FacebookShareAction. Now the goal of the Dependency Inversion Principle is to not depend on concrete methods of the share action classes, like sharePostOnTwitter and sharePostOnFacebook. Instead, the Distributor class would define an interface called Sharing which is implemented by TwitterShareAction and FacebookShareAction. It declares the abstract method sharePost. The Distributor class doesn’t have to know the details of the concrete share actions. It just calls the sharePost method.
By using the SOLID Principles, code complexity is reduced by being more explicit and straightforward; readability is greatly improved; coupling is generally reduced; and your code has a better chance of cleanly evolving.
Published at DZone with permission of Ayush Tiwari, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.