0% found this document useful (0 votes)
52 views28 pages

SOLID - by Rom OD

Uploaded by

Messhu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
52 views28 pages

SOLID - by Rom OD

Uploaded by

Messhu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 28

SOLID Principles

Reminder for clean code

with Coffee machine


Analogies
Single Responsibility
Analogy

Imagine our coffee machine could


serve Coffee, Macchiato, Ristretto...

The purpose is to not alter the class


in charge of making Coffee if we want
to change the way to make a Ristretto
Code example
Open-Closed Principle
Analogy

Machine Brew
coffee

updating

Machine Brew Machine Brew


latte coffee
latte
Someday you have to improve your machine to also do
Capucino you don’t want to affect your brew method
Violating OCP

This class is closed for extension because if we want to add


support for another type of coffee, like Latte, we would
need to modify the BrewCoffee method to handle the new
case.
using polymorphism

With this approach, adding support for a new type of coffee


(e.g., Cappuccino) becomes as simple as creating a new
class that implements IBrewable and adding it to the
CoffeeMachine.
Liskov Substitution Principle
Analogy

CoffeMachine

:CoffeeMachine

PremiumCoffeeMachine

Clients should be able to use both seamlessly


Violating LSP

The PremiumCoffeeMachine's Brew method includes an


additional step (grinding beans) that the CoffeeMachine
does not perform. This violates LSP because substituting
a PremiumCoffeeMachine for a CoffeeMachine changes
the behavior of the program in an unintended manner.
Refactoring with Composition

While inheritance is a powerful tool for code reuse


and expressing "is-a" relationships, it can
sometimes lead to tight coupling between classes,
making the code harder to maintain and extend.

Composition, on the other hand, promotes loose


coupling and flexibility, allowing for easier
modification and extension of the codebase

To adhere to LSP, we can refactor the design to use


composition.

This involves creating separate components for


brewing and grinding, which can be combined as
needed.
1 - Defining Components

2 - Implement components
3 - Composing
Interface Segregation Principle
Analogy
interface ICoffee {
GrindCoffeeBeans();
BoilWater();
PourCoffee();
AddCream();
}

cream adder interface IGrinder {..} no cream adder


:IGrinder :IGrinder
:IKettle interface IKettle {..}
:IKettle
:IPourer :IPourer
interface IPourer {..}
:ICreamAdder
interface ICreamAdder {...}

With small interface, we can compose any sort of coffee


machine
Violating ISP

This example forces any implementing class to define


methods for adding cream, boiling water, grinding coffee
beans, and pouring coffee, even if the class does not
require all these functionalities, leading to unnecessary
implementation details and increased complexity
Split in small interface

This way we can even implement a simple ketlle and


serve hot water
Understand DI & IoC
Analogy
:IWaterProvider

A coffee machine needs water to boil,


but it doesn't matter who supplies the
water

@romain-od
How it’s works

@romain-od
Abstraction

@romain-od
Low-Level

@romain-od
High-Level

@romain-od
Dependence
Registration

OR

OR

Which one choose ?

@romain-od
Singleton
A single instance of the dependency is created
and shared across the entire application.

The singleton approach is


lifetime, it means every day
you use the coffee machine
you use a carafe to fill the
water tank.

Life of coffeemachine

@romain-od
Scoped
A new instance of the dependency is created
once per scope. A scope can be thought of as a
unit of work.

The scoped approach, it’s like


today I committed to use the
carafe, tomorrow I will use a
bottle.

Monday Tuesday Wednesday

@romain-od
Transient
A new instance of the dependency is created
each time it is requested

With transient approach, we


take the first provider we
found to fill the water tank
each time we use the coffee
machine.

8:00 8:45 10:00 12:35

@romain-od
What about reference ?

Can we use Scoped service in Singleton?


Can we use Singleton service in Transient?
Can we use Transient service in Scoped?
Can we use Singleton service in Scoped?
Can we use Scoped service in Transient?
...

@romain-od
Captive Dependency
Captive Dependency is a dependency with an
incorrectly configured lifetime. A service
should never depend on a service that has a
shorter lifetime than its own.

Transient Scoped Singleton

Transient

Scoped

Singleton

@romain-od
Can we use a different provider to fill the
Water Tank and rinse the filter ?

.NET 8 introduce keyed services


Keyed services managing Dependency Injection (DI) services by
associating them with keys for registration and retrieval.

We use methods AddKeyedSingleton, AddKeyedScoped, or


AddKeyedTransient to register a service with a specific key.

We use the [FromKeyedServices] attribute and specify the


corresponding key to access a registered service

@romain-od
Keyed services

@romain-od
Thanks for reading

You might also like