Skip to main content

SOLID - Design patterns

SOLID este un acronim de la:
SRP - The Single Responsibility Principle: o clasa ar trebui sa aibe o singura responsabilitate;
OCP - The Open Closed Principle: o clasa trebuie sa fie deschisa la extindere fara a necesita modificarea ei;
LSP - The Liskov Substitution Principle: clasele derivate pot sa fi inlocuite de clasele de baza;
ISP - The Interface Segregation Principle: interfete cu granulatie fina, specifice unei anumite responsabilitati, specifice unui anumit client;
DIP - The Dependency Inversion Principle: dependintele trebuie sa fie definite prin interfete si clase abstracte si nu prin clase concrete;

The Single Responsibility Principle( SRP)
"There should never be more than one reason for a class to change." — Robert Martin

O clasa trebuie sa faca doar in singur lucru si nimic mai mult. Fiecare clasa in parte trebuie sa aiba o singura responsabilitate. O clasa care are o singura responsabilitate este mai usor de modificat, mult mai usor de inteles si mai usor de testat. Totodata daca o clasa are mai multe responsabilitati avem mai mari sanse ca acelasi cod sa apara si in alte clase.
O clasa care are mai multe functionalitati este prea mare, face prea multe si este mult prea complicata. Cel mai usor lucru pentru a rezolva aceasta problema este sa facem split la clasa.

Open Closed Principle( OCP)
"Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification." — Robert Martin

O clasa trebuie sa fie deschisa spre extindere si inchisa spre modificare. Comportamentul unei clase ar trebui sa fie modificat prin mostenire si prin compozitie. Principala idee este ca functionalitatile logice sa fie cat mai bine definite, iar daca este cazul acestea sa fie mutate in alte clase si referite prin intermediul interfetelor.
De exemplu daca avem o clasa ce face validarea unui obiect, aceasta nu trebuie sa contina si regulile de validare. Acestea pot sa fie referite prin intermediul unei interfete si transmise prin constructor. In felul acesta daca regulile se modifica, nu o sa fie necesar sa modificam clasa care face validarea. Astfel regulile de validare o sa poata fi schimbate mult mai usor.

Liskov Substitution Principle( LSP)
"Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it." — Robert Martin

Ideea de baza este ca putem inlocui instanta unei clase cu subclasa fiu si totul sa functioneze normal. Nu este tocmai usor de facut acest lucru, deoarececlasa poate sa aibe un set de actiuni specifice care sa nu se mai regăsească in clasa fiu.
Un bun exemplu este exemplu clasic cu dreptunghiul si patratul, unde un patratul poate sa mosteneasca din dreptunghi dar laturiile deja difera ca denumire si logica.
Dar asta nu inseamna ca nu trebuie sa incercam sa respectam LSP. Trebuie sa incercam sa facem clasele de baza cat mai generice. Aceasta regula nu se aplica mereu, dar exista cazuri cand este foarte utila. De foarte multe ori nu putem modela obiectele exact la fel cum sunt in lumea reala prin mostenire, dar ne putem folosi de compozitie pentru acest lucru.

Interface Segregation Principle( ISP)
"Clients should not be forced to depend upon interfaces that they do not use." —Robert Martin
Fiecare interfata trebuie sa fie cat mai simpla si sa defineasca un singur lucru( sa fie specifice). In cazul in care avem o interfata foarte mare, cel mai bine este ca aceasta sa fie sparta in functie de ce responsabilitati are. In cazul in care avem mai multe functionalitati intr-o clasa, atunci in clasa respectiva va fi nevoie sa implementam toate interfetele de care avem nevoie. De multe ori daca avem o interfata foarte mare, clientul nu o sa aibe nevoie sa implementeze toate functiile, ci doar o parte din ele, a.i. se ajunge ca o mare parte din metode sa nu fie implementate.

Dependency Inversion Principle( DIP)
"A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions." — Robert Martin
Daca o clasa are dependinte fata de alte clase, acestea ar trebui sa fie prin interfete si clase abstracte si nu prin clase concrete. Prin acest mod clasele se pot modifica mult mai usor, o sa fie mult mai decuplata si mult mai usor de testat.
Daca respectam acest principiu, putem folosi Dependency Injection fara nici o problema. Totodata putem mult mai usor sa separam nivelele unei aplicatii.

Comments

  1. Ma tem ca Liskov substitution principle e un pic invers: se poate inlocui o instanta a unei clase de _baza_ cu o instanta a unei clase derivate (subclasa) a.i. toate proprietatile (preconditiile, postconditile, invariantii) aferente raman valabile (la nivel semantic, nu doar sintactic)

    ReplyDelete
  2. Catdespre Dependency Inversion, se poate formula si altfel, mai intuitiv: daca o clasa A are nevoie de serviciile unei alte clase B, prima clasa ar trebui sa defineasca o interfata I si doar sa se asigure ca obtine (sau i se furnizeaza) o instanta ce expune interfata I (care in practica va fi implementata de clasa B sau de un adapter ce apeleaza B).

    ReplyDelete

Post a Comment

Popular posts from this blog

Windows Docker Containers can make WIN32 API calls, use COM and ASP.NET WebForms

After the last post , I received two interesting questions related to Docker and Windows. People were interested if we do Win32 API calls from a Docker container and if there is support for COM. WIN32 Support To test calls to WIN32 API, let’s try to populate SYSTEM_INFO class. [StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public uint dwOemId; public uint dwPageSize; public uint lpMinimumApplicationAddress; public uint lpMaximumApplicationAddress; public uint dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public uint dwProcessorLevel; public uint dwProcessorRevision; } ... [DllImport("kernel32")] static extern void GetSystemInfo(ref SYSTEM_INFO pSI); ... SYSTEM_INFO pSI = new SYSTEM_INFO(

Azure AD and AWS Cognito side-by-side

In the last few weeks, I was involved in multiple opportunities on Microsoft Azure and Amazon, where we had to analyse AWS Cognito, Azure AD and other solutions that are available on the market. I decided to consolidate in one post all features and differences that I identified for both of them that we should need to take into account. Take into account that Azure AD is an identity and access management services well integrated with Microsoft stack. In comparison, AWS Cognito is just a user sign-up, sign-in and access control and nothing more. The focus is not on the main features, is more on small things that can make a difference when you want to decide where we want to store and manage our users.  This information might be useful in the future when we need to decide where we want to keep and manage our users.  Feature Azure AD (B2C, B2C) AWS Cognito Access token lifetime Default 1h – the value is configurable 1h – cannot be modified

What to do when you hit the throughput limits of Azure Storage (Blobs)

In this post we will talk about how we can detect when we hit a throughput limit of Azure Storage and what we can do in that moment. Context If we take a look on Scalability Targets of Azure Storage ( https://azure.microsoft.com/en-us/documentation/articles/storage-scalability-targets/ ) we will observe that the limits are prety high. But, based on our business logic we can end up at this limits. If you create a system that is hitted by a high number of device, you can hit easily the total number of requests rate that can be done on a Storage Account. This limits on Azure is 20.000 IOPS (entities or messages per second) where (and this is very important) the size of the request is 1KB. Normally, if you make a load tests where 20.000 clients will hit different blobs storages from the same Azure Storage Account, this limits can be reached. How we can detect this problem? From client, we can detect that this limits was reached based on the HTTP error code that is returned by HTTP