SOLID (object-oriented design)
·
SOLID (single responsibility, open-closed, Liskov substitution,
interface segregation and dependency inversion)
- easy to maintain and
extend over time
· to remove code smells by
causing the programmer to refactor the
software's source code until it
is both legible and extensible.
·
It is part of an overall strategy of agile and Adaptive Software
Development.
SOLID Overview
Initial
|
Stands for
|
Concept
|
S
|
SRP
Single
responsibility principle
|
- a class should have only a single
responsibility
- A class should concentrate on doing one thing
- There should never be more than one reason for a class to change.
- The SRP says a class should focus on doing one thing, or have one responsibility. This doesn’t mean it should only have one method, but instead all the methods should relate to a single purpose (i.e. should be cohesive).
- A class that adheres to the SRP should be easier to change than those with multiple responsibilities
- . If we have calculation logic and database logic and display logic all mixed up within one class it can be difficult to change one part without breaking others.
- Mixing responsibilities also makes the class harder to understand, harder to test, and increases the risk of duplicating logic in other parts of the design (decreases cohesion, functionality has no clear place to live).
- Violations of the SRP are pretty easy to notice: the class seems to be doing too much, is too big and too complicated. The easiest way to fix this is to split the class.
- deciding how to define the single responsibility. There may be many ways to dissect a feature into responsibilities, but the ideal way is to use responsibilities that are likely to change independently, hence the official description: "A class should have one, and only one, reason to change".
|
O
|
OCP
Open/closed
principle
|
- Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
- Change a class’ behaviour using inheritance and composition
- The idea is that we can use OO techniques like inheritance and composition to change (or extend) the behaviour of a class, without modifying the class itself.
- Following the OCP should make behaviour easier to change, and also help us avoid breaking existing behaviour while making changes. The OCP also gets us to think about the likely areas of change in a class, which helps us choose the right abstractions required for our design.
- If you find you need to modify a similar area of code all the time (for example, validation rules) then it’s probably time to apply the OCP and abstract away the changing part of the code. Another sign of a potential OCP violation is switching on a type — if another type is created then we’ll have to alter the switch statement.
|
L
|
LSP
Liskov
substitution principle
|
- Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
- Subclasses should behave nicely when used in place of their parent class
- We should be able to substitute an instance of a subclass for its parent class and everything should continue to work. Easy right?
- Advised to favour composition over inheritance. Ensuring a subclass works in any situation the parent does is really hard work, and whenever you use inheritance its a good idea to keep the LSP firmly in mind.
- to keep very aware of the LSP whenever using inheritance, including considering avoiding the problem using composition where appropriate.
|
I
|
ISP
Interface
segregation principle
|
- Clients should not be forced to depend upon interfaces that they do not use.
- Keep interfaces small and cohesive
- The ISP is about keeping interfaces (both interface, and abstract class types of interfaces*) small and limited only to a very specific need (a single responsibility even :)).
- If you have a fat interface then you are imposing a huge implementation burden on anyone that wants to adhere to that contract. Worse still is that there is a tendency for class to only provide valid implementations for a small portion of a fat interface, which greatly diminishes the advantages of having an interface at all
- The way to fix violations like this is to break down interfaces along the lines of responsibilities and apply the SRP.
- If we need all the features then we can implement both interfaces, but we should not be forcing clients to fake or throw in implementations that are meaningless to them.
|
D
|
DIP
Dependency
inversion principle
|
- High level modules should not depend upon low level modules. Both should depend upon abstractions. 0r Abstractions should not depend upon details.
- Details should depend upon abstractions.Use lots of interfaces and abstractions
- The DIP says that if a class has dependencies on other classes, it should rely on the dependencies’ interfaces rather than their concrete types. The idea is that we isolate our class behind a boundary formed by the abstractions it depends upon. If all the details behind those abstractions change then our class is still safe.
- This helps keep coupling low and makes our design easier to change.
- Dependency Injection is about getting other code to insert the actual dependency instances into our class, so we don’t even have the client class newing up any of the concrete instances. This completely isolates our class and makes change and reuse much easier.
- The other side of the DIP relates to dependencies between high and low level modules in layered applications. For example, a class accessing the database should not depend on a UI form used to display that data. Instead the UI should rely on an abstraction (or abstractions) over the database access class. Traditional application layers (data, logic, ui) seem largely replaced by MVC, onions and hexagons these days, so I tend to think about the DIP entirely from the point of view of abstracting dependencies.
|