Skip to content

Instantly share code, notes, and snippets.

@christian-fei
Last active March 22, 2018 12:36
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christian-fei/c5f26af97fd2a9644a63 to your computer and use it in GitHub Desktop.
Save christian-fei/c5f26af97fd2a9644a63 to your computer and use it in GitHub Desktop.
SOLID

SOLID

sources

Principles

Single Responsibility

There should never be more than one reason for a class to change.

A responsibility for a class is a reason of change. Mixing responsabilities causes strict coupling, unability or difficulty to reuse modules, fragility (changes break other 'unrelated' parts of the system). When a class has more than one responsability, the responsabilities become coupled together, and may get in the way of each other when the requirements change. A class/software becomes fragile.

The SRP helps keep classes and methods small, maintainable, focussed, and easier to understand.

Example

The Shape class has two methods: draw and area. draw is related to the GUI, area is related to calculation. This Shape may change for different reasons, coming from differnet "departments" (marketing,design,po,etc.). Having unrelated responsabilities coupled together impairs the class to change, in addition the system may break for unexpected reasons.

Open Closed

Entities should be open for extension, but closed for modification.

Software changes during its lifetime. The question is: What's a good way to build software that is maintainable and stable against change?

A desired approach to handle change is to adding new, fresh code, not by modifying old code that is working and tested.

What does "open for extension" and "closed for modification" mean:

  • open for extensions means that the module (or the behaviour) can be extended to meet the new requirements.
  • closed for modification means that changes to existing modules are not allowed/desired.

But how can one achieve this principle if a module that has to be extended in its behaviour cannot be modified? Through abstraction.

In other words: a module can be closed for modification if it depends only on an abstraction, that can be extended (open for modification) through inheritance. Abstract classes, interfaces, etc help us to achieve this.

This principle is achieved through the use of interfaces, because the interface is closed for modification, a module that uses this interface implements, at a minimum, the required "contract"/methods.

This is not necessarily achieved only through inheritance, a software can also be conform to ocp if not OO.

It depends on the programmer how much encapsulation is needed/desired for a certain component, knowing about the client's needs, likely to change requirements, etc.

Liskov Substitution

This principle states, that if a class X is a subtype of Y, then objects of class X can be replaced by any other subtype of Y. The important part, besides from being able to substitute these subtypes, is that the behaviour should not change. It is also called behavioral subtyping.

Example

A typical example of LSP is the Rectangle-Square-Problem:

In practice a Square is a subtype of a Rectangle. The getters and setters for the Rectangle type are defined for height and width, thus implemented in Square. Though a square should maintain the width and height equal, so we would override the setter of a specific property (height/width) and set also the other to be a consistent square. But this is not the expected behaviour of the type Rectangle. It is not fully substitutable with a Square, since it behaves differently.

Interface Segregation

This principle states: do not depend on things you don't need. Or better: don't force your clients to depend on things the don't need. An important related concept is that interfaces have more to do with classes that use them, that with classes that implement them. This is why interfaces should be focused, specialized. An interfaces should represent a single "responsability". How detailed you want to make your interfaces is up to you.

Example

An example could be the repository pattern:

One could create a full-fledged CRUD repository and force their clients to implement every operation. Some may only need to read from the repo, but they have to implement the other operation too. This problem could be solved with a separate ReadRepository and WriteRepository. In addition you could extend both and create the original CRUDRepository by extending both ReadRepository and WriteRepository.

Dependency Inversion

High-level modules should not depend on low-level modules, both should depend upon abstractions. Abstractions should not depend on details, details should depend on abstractions.

High-level modules contain business logic, flows and make decisions, these should not depend on low-level modules. It's the high-level modules that should have the control over low-level modules, certainly not adapt to changes of them. Otherwise this limits the reuse of high-level modules, causes coupling, and other 'diseases' like rigid (due to hardcoded dependencies), fragile (changes cause unexpected things), immobile (difficult to reuse) software.

The dependency inversion is often achieved by following the OCP in OO, in procedural and FP it is achieved through other mechanisms. Other patterns I read are used are for example the Factory Pattern.

Example

The example used to describe the concept of high/low-level modules and the DIP is a simple Copy program.

The Copy program contains the logic, business rules to do its job. Other low-level components that do the work are the FileWriter and the KeyboardReader. In the 'rigid' example these dependencies are coupled with the high-level module, and cause difficulty to change when new requirements arrive. The code slowly rots and becomes difficult to maintain as new requirements are implemented and the software gets out of hand.

It could be solved with decoupling the low-level modules that cause this rigidity, by inverting the strict dependency that points toward the Copy program by placing an interface in between.

CopyProgram --> Writer/ReaderInterface <-- FileWriter,VideoWriter,KeyboardReader,FileReader etc.

To understand DIP better I used these resources:

:wq

Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment