We at Voxteneo as a web development company follow the agile software development paradigm and what better way to obtain cross-functionality and flexibility than committing to the S.O.L.I.D principles. In software development the best way to the finish line is rarely a straightforward sprint. In most cases a careful pace, consisted of steps composed of additional principles and implementation goals, is required. What these principles provide is moving towards a loosely coupled code that operates cohesively and encapsulates the needed business logic.
“What does S.O.L.I.D stand for?”
The Single responsibility principle regards the creation of classes during the design phases of an application and identifying the correct responsibilities of each class requires a great deal of understanding of the application’s business logic. Designing the architecture in this manner will ensure that when changes need to be made regarding certain business rules, they won’t break the code in different places.
The Open-Closed Principle indicates how a system can be extended by modifying the behavior of individual classes, without having to modify the class itself. As the principle dictates, software entities should be open for extension, but closed for modification. This helps you create well-encapsulated, highly cohesive systems. There is a funny analogy regarding this principle, it states “Open chest surgery is not required when putting on a coat.” Meaning, we as programmers should not risk altering core functionality to add a simple feature and potentially compromise data integrity.
Liskov Substitution Principle states that objects in a program should be replaceable with instances of their subtypes without altering the correctness of the program. This principle says that you should not violate the intent or semantics of the abstraction that you are inheriting from or implementing. That is why you model your classes based on behaviors not on properties and model your data based on properties and not on behaviors. With this knowledge in hand, evaluating LSP adherence can be a great tool in determining when composition is the more appropriate mechanism for extending existing functionality, rather than inheritance.
The Interface Segregation Principle helps to make your system easy to understand and use. It says that you should not force a client to depend on an interface that the client does not need. Many client-specific interfaces are better than one general-purpose interface. In other words, you should not have to implement methods that you don’t use. Enforcing ISP gives you low coupling, and high cohesion.
“The Dependency Inversion Principle helps you to understand how to correctly bind your system together. It tells you to have your implementation detail depend on the higher-level policy abstractions, and not the other way around.” This principle could be rephrased as, use the same level of abstraction at a given level. When this principle is applied it means the high level classes are not working directly with low level classes, they are using interfaces as an abstract layer. As one analogy goes “Would you solder a lamp directly to the electrical wiring in a wall?”, that is a solution of course, but a rather impractical one, a better approach is to use a socket as an “interface” that is considered a contract for connecting appliances to the grid.
“Test, test, test!”
One might wonder, what is the ultimate benefit of following these principles?
Given there are a few benefits, but the most prominent one is testability.
In Agile development there is simply no way around it, practicing Test Driven Development is a must and there is no better way to obtain testable code than to follow these 5 principles. Highly cohesive, loosely coupled designs are testable. When dependencies can be replaced or stubbed out, you can test classes and methods in isolation from one another. The Single Responsibility and Interface Segregation principles enable this. Being able to mock or stub a dependency is a powerful and important technique, and it ultimately requires the Liskov Substitution principle and to a lesser extent, the Dependency Inversion principle.
“use composition over inheritance”
The lesson to be learnt here is that the road to stable development, that is prone to dynamic change in the business logic, is paved with “solid” blocks of tested quality and consistency. There are subtleties in the approach, but a general recommendation would be to most often use composition over inheritance. Bare in mind though that composition is not a panacea and a deeper analysis is required to decide whether it is to be used over inheritance. Composition over inheritance, however clichéd it has been as of lately in the web development community, it might just end up making our lives easier in the long run.