Single-Responsibility Principle (SRP)
The single responsibility principle states that every module or class should have responsibility over a single part of the functionality provide by the software, and that responsibility should be entirely encapsulated by the class, module or function. All its services should be narrowly aligned with that responsibility 1 .
Everything should do just one thing
Orthogonality: … We want to design components that are self-contained: independent, and with a single, well-defined purpose ([…] cohesion). When components are isolated from one another, you know that you can change one without having to worry about the rest. @Hunt1999
Cohesion is a measure of the strength of association of the elements inside a module. A highly cohesive module is a collection of statements and data items that should be treated as a whole because they are so closely related. Any attempt to divide them up would only result in increased coupling and decreased readability 2 .
A class should have only one reason to change 3
SRP brings two benefits: @Hunt1999
- Gain on productivity
- Reduction of risk
We can improve the overall team productivity since the development and testing time for small components are relative short compare to larger counterparts. Smaller module means lesser code, easier to design and simpler to do unit test. Therewith, such component has great usability because it has a specific and well-tested responsibility, plus it doesn’t overlap with other modules.
In debugging sense, the job become simpler. Base on the fact that the component is rather isolated from the rest of the codebase, the symptoms are likely to be contained in one area. These components have a high chance to be well-tested since the tests themselves are not sophisticated. Besides the simplification of debugging process, this will reduce the reliance on particular vendor, product, or platform since the interface of them are insulated from the rest of the codebase.
Guideline: Prefer cohesive software entities. Everything that does not strictly belong together, should be separated. 4
Open-Closed Principle (OCP)
Software artefacts (classes, modules, functions etc.) should be open for extension, but closed for modification. 5
The main approach is procedural which is moving the corresponding member function to the outer scope and make it a non-member or free function.
Dynamic polymorphism with code injection and metaclasses could work on Object-Oriented approach without breaking SRP. 6
Guideline: Prefer software design that allows the addition of types or operations without the need to modify existing code. 4
Liskov Substitution Principle (LSP)
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 the 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. 7
It is essentially about the behavioural subtyping, or IS-A relationship. The basic characteristics of this relationship are:
- Contravariance (more derived type could change the property defined in less derived type) of method arguments in a subtype
- Covariance (less derived type could change the property defined in more derived type) of return types in a subtype
- Preconditions cannot be strengthened in a subtype
- Postconditions cannot be weakened in a subtype
- Invariants of super type must be preserved in a subtype
We can strengthen or even enforce the checking of these conditions using 202206301938#.
Guideline: 4
- Make sure that inheritance is about behaviour not about data.
- Make sure that the contract of base types is adhered to.
- Make sure to adhere to the required concept.
Interface Segregation Principle (ISP)
Clients should not be forced to depend on methods that they do not use. 3
Many client specific interfaces are better than one general-purpose interface. 8
Write shy codes: don’t reveal yourself to others, and don’t interact with too many people. The Pragmatic Programmer By that, the interaction between the modules or interfaces is limited so if one get comprised, the others remained unaffected. Make sure the implementation of the interface is adhered to the 202207031635#.
Guideline: Make sure interfaces don’t induce unnecessary dependencies 4
Dependency Inversion Principle (DIP)
The Dependency Inversion Principle (DIP) tells us that the most flexible systems are those in which source code dependencies refer only to abstractions, not to concretions. 9
High-level modules should not depend on low-level modules. Both should depend on abstractions
Abstractions should not depend on details. Details should depend on abstractions. 3
Make a local inversion of dependencies in the lower level abstraction and then move it to the higher level abstraction. The user should not directly interact with low level modules, instead they should rely on a general contractor with high abstractions, that encapsulate such dependencies, and use the module on the user’s behalf. The Pragmatic Programmer See more in 202207041054# or 202207041156#.
Guideline: Prefer to depend on abstractions (i.e. abstract classes or concepts) instead of concrete types. 4