In addition to the syntax of a programming language and the understanding of the most important libraries and frameworks, other methodologies – such as design patterns – belong to the fundamentals of software development. Aside from a design pattern, design principles are also a helpful tool in the development of software. SOLID is an acronym for five such design principles, which help developers to design software more understandable, more flexible and more maintainable.
In larger software projects, a great number of function blocks exist that are connected to each other via inheritance and references. These units interact by the calls of the function blocks and their methods. The interaction of the code units can unnecessarily complicate the extending or finding of errors if designed wrongly. In order to develop sustainable software, the function blocks should be modeled in such a way that they are easy to extend.
Many design patterns apply the SOLID principles to suggest an architectural approach for the respective task. The SOLID principles are not to be understood as rules, but rather as advice. They are a subset of many principles that an American software engineer and lecturer Robert C. Martin (also known as Uncle Bob) presented in his book (Amazon advertising link *) Clean Architecture: A Craftsman’s Guide to Software Structure and Design. The SOLID principles include:
- Single Responsibility Principle (SRP)
- Open/Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)
The principles shown here are hints that make it easier for a developer to improve code quality. The effort pays for itself after a short time, because changes will be easier, tests and debugging will be faster. Thus, the knowledge of these five design principles should be part of every software developer’s basic knowledge.
Single Responsibility Principle (SRP)
A function block should have only one responsibility. If the functionality of a program is changed, this should have effects only on few function blocks. Many small function blocks are better than a few large ones. The code appears at first sight more extensive, but it is easier to organize. A program with many smaller function blocks, each for a special task, is easier to maintain, than few large function blocks, claiming to cover everything.
Open/Closed Principle (OCP)
According to the Open/Closed Principle, function blocks should be open for extensions but closed for changes. The implementation of extensions should only be achieved by adding code, not by changing existing code. A good example of this principle is inheritance. A new function block inherits from an existing function block. New functions can thus be added without having to change the existing function block. It is not even necessary to have the program code.
Liskov Substitution Principle (LSP)
Liskov Substitution Principle requires that derived function blocks must always be usable in place of their basic function blocks. Derived function blocks must behave like their basic function blocks. A derived function block may extend the base function block, but not restrict it.
Interface Segregation Principle (ISP)
Many customized interfaces are better than one universal interface. Accordingly, an interface may only contain those functions that really belong together closely. Comprehensive interfaces create links between otherwise independent program parts. Thus, the Interface Segregation Principle has a similar goal as the Single Responsibility Principle. However, there are different approaches to the implementation of these two principles.
Dependency Inversion Principle (DIP)
Function blocks are often linearly interdependent in one direction. A function block for logging messages calls methods of another function block to write data to a database. There is a fixed dependency between the function block for logging and the function block for accessing the database. The Dependency Inversion Principle resolves this fixed dependency by defining a common interface. This is implemented by the block for the database access.
Conclusion
In the following posts, I will introduce the individual SOLID principles in more detail and try to explain them using an example. The sample program from my post IEC 61131-3: The Command Pattern serves as a basis. With each SOLID principle, I will try to optimize the program further.
I will start shortly with the Dependency Inversion Principle (DIP).