SOLID
2023-03-31 Class Object-Oriented Programming OOP DesignSOLID are five object-oriented design principles, compiled by Robert C. Martin in his work Design Principles and Design Patterns. First letters of the acronym represent those principles:
- S – Single-responsibility principle
- O – Open-closed principle
- L – Liskov substitution principle
- I – Interface segregation
- D – Dependency Inversion principle
Their goal is to simplify reuse and make software easier to maintain and extend.
From the paper, here are symptoms of rotting design:
- Rigidity - software is difficult to change, which tends to cascade to many other changes
- Fragility - tendency to break in many places when it is changed
- Immobility - difficulty to reuse part of a project in another
- Viscosity - right way to do something (along a design) is difficult to do, so engineers tend to use hacks to fix problems
Most of the rotting is caused by poor design, where changing requirements cause unplanned dependencies. In order to manage them, it is necessary to keep them under control. Following principles were designed for that purpose.
Single-responsibility Principle
A class should have one and only one reason to change, meaning that a class should have only one job
Using this principle leads to smaller classes with better defined responsibility. It is much easier to determine whether a change fits in the class.
Open-closed Principle
Objects or entities should be open for extension, but closed for modification
The class should be easily extensible without modifying the class itself. It might be sometimes hard to achieve, but when done, it significantly improves maintenance.
This also avoids changes in existing code (and anything that inherits it) that currently works. If code is not changed, it is unlikely to break.
Following techniques can be used to obtain this property:
- Dynamic Polymorphism - use interfaces and put specific code into respective class
- Static Polymorphism - templates or generics
Liskov substitution principle
Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T
Coming from Barbara Liskov‘s type theory. It simply means that anything that works on a class should be also working same way on any subclass. The interface can be considered a “contract” with users of the class, and breaking it in subclasses can easily lead to problems with polymorphism.
Good way to verify this is achieved is through the test suite. All tests that pass in parent class should also pass with the child.
Interface segregation principle
A client should never be forced to implement an interface that it doesn’t use or clients shouldn’t be forced to depend on methods they do not use
Interfaces should not include unrelated things and force a class to implement more than necessary. The goal is have more smaller interfaces, which are more specific.
Dependency Inversion principle
Entities must depend on abstractions not on concretions. It states that the high level module must not depend on the low level module, but they should depend on abstractions
Prefer interfaces over dependency on specific class. Also try to avoid creating dependency in the class itself, rather have it pushed from outside.