SOLID principles in programming

Software development is a complex process that requires careful planning, architecture and design. One of the key objectives in software development is to ensure that the software is flexible, extensible and sustainable. This is where the SOLID principles come in, which are a set of five core principles of object-oriented programming (OOP) that help to create high-quality and highly scalable software. These principles were proposed by Robert C. Martin and are known by the acronym SOLID. In this article, we will look at each of these principles in detail.

Well-designed software requires a high-quality codebase that is easily readable, understandable, maintainable (for adding/changing features, fixing bugs), and extensible for the future. This saves development time and resources.

SOLID principles:

S – Single Responsibility Principle

O – Open/Closed Principle

L – Liskov Substitution Principle

I – Interface Segregation Principle

D – Dependency Inversion Principle

Single Responsibility Principle (SRP)

The single responsibility principle states that each module, class, or function in a computer program should have responsibility for one part of that program’s functionality. They should also encapsulate this part and their services should be closely related to this responsibility. This ensures that classes are smaller, clearer, easier to understand, and code maintenance is simpler. If a class multitasks, it becomes complex and error-prone.

Examples of responsibilities that may need to be separated in software in general:
formatting, parsing, mapping, validation, logging, persistence, notification, error handling, class selection/instance, etc.

Open/Closed Principle (OCP)

The principle of openness and closure tells us that modules, classes, functions should be open for extension and closed for modification at the same time. This means that we should be able to change the behavior of a module/class without having to interfere with its source code. We achieve this by using interfaces, abstract classes or inheritance. In this way, we minimize the risk of introducing errors into existing code when adding new features.

How to apply OCP:

Add new functionality by creating new derived classes that inherit from the original base class and allow the client to access the original class through an abstract interface via compositional design patterns, such as the Strategy pattern. Instead of changing the existing functionality, create new derived classes and leave the original implementation of the class as is. This principle will be violated if you ever have to modify the base class.

Liskov Substitution Principle (LSP)

Liskov’s principle of substitution states that an object of a given class should be able to be replaced by an instance of its derived classes without changing the behavior of the program. That is, if we have a class A and a derived class B, we should be able to use B wherever we expect an object of type A. This principle assures us that inheritance and polymorphism work correctly and consistently.

If we can successfully replace an object/instance of the parent class with an object/instance of the child class without affecting the behavior of the base class instance, we follow the LSP. When this principle is violated, it usually leads to a lot of additional conditional logic scattered throughout the application code to check if an object is of a certain type.

As the size of the application grows, duplicate and scattered code becomes a breeding ground for bugs. A very common violation of this principle is partial implementation of interfaces or functionality of the base class, where not all methods or properties are implemented, and instead, we throw an exception (for example, NotImplementedException).

Such implementations do not belong in code that is delivered to third parties.
Such implementations do not belong in code that is delivered to third parties.

In code that you know will only be used by one client that you can monitor, that’s fine. But in shared code, or even worse, within a framework that is delivered to third parties, such implementations should not occur.

If an interface has more features than you need, use the Interface Segregation Principle (ISP) to create a new interface that contains only the features that your client code needs and that you can fully implement.

Interface Segregation Principle (ISP)

The principle of interface segregation tells us that we should create multiple specific interfaces rather than one large, generic interface. This helps us minimize dependencies and ensure that classes only implement the methods they need and are not forced to implement unnecessary methods. Simply put, clients should not be forced to implement any methods they don’t use.

Dependency Inversion Principle (DIP)

The principle of inverse dependency states that modules/classes at a higher level should not depend on modules/classes at a lower level. Instead, both should depend on abstractions. Further, abstractions should not depend on implementation details, those details should depend on abstractions.

When a class knows the design and implementation of another class, the risk that changes in one class will disrupt the other class increases. Always try to keep modules/classes at different levels as minimally connected as possible. To do this, we have to make both dependent on abstractions instead of knowing each other. This way, the code is flexible and we can easily change it without having to change all the places where the class is used.

Conclusion

Software design principles are recommendations that developers follow when creating software in order to generate clean and sustainable code. It is a collection of techniques and best practices recommended by many well-known industry experts and authors.

SOLID OOP principles are an important part of object-oriented software design. Adhering to them helps us create high-quality, sustainable, and easily extensible code.

The Single Responsibility Principle ensures that classes have a single responsibility.
Open/Closed Principle allows us to change the behavior of classes without modifying the code.
The Liskov Substitution Principle ensures that derived classes can be used in place of the parent class. Interface Segregation Principle helps us minimize dependencies between classes.
The Dependency Inversion Principle ensures that we depend on abstractions, not concrete implementations.

Do you know Solid principles and Java programming language? If you are looking for a job, for example. as a Java developer, see our employee benefits and respond to the latest job offers.

About the author

Jozef Wagner

Java Developer Senior

Viac ako 10 rokov programujem v Jave, momentálne pracujem v msg life Slovakia ako Java programátor senior a pomáham zákazníkom implementovať ich požiadavky do poistného softvéru Life Factory. Vo voľnom čase si rád oddýchnem v lese, prípadne si zahrám nejakú dobrú počítačovú hru.

Let us know about you