Sunteți pe pagina 1din 8

SOLID

SOLID e un acronim ce reprezinta 5 principii de baza in programarea orientata pe


obiecte, aplicind aceste principii ne permit sa cream un sistem care este simplu de
mentinut si extins.

Aceste principii sunt:


S (SRP - Single responsibility principle) o clasa trebuie sa aiba doar o responsabilitate;
O (OCP - Open closed principle) clasa trebuie de extins, dar nu de modificat. Entitatile,
modulele trebuie sa fie deschise pentru extindere si inchise pentru modificari, trebuie de
proiectat astfel modulele, ca ele sa nu se modifice niciodata cind apare o cerinta de
schimbare trebuie de extins functionalitatile prin adaugare de cod (nu de modificat cel
existent). Conformitate cu acest principiu nu efera cele mai mari beneficii oferite de OOP
si anume reutilizarea si mentenanta, insa comformitatea ar putea fi atinsa numai prin
utilizarea unui limbaj orientat pe obiecte.
L (LSP - Liskov substitution principle) clasele derivate trebuie sa poata inlocui clasele de
baza;
I (ISP - Interface segregation principle) mai multe interfete decit o interfata de baza.
Favorizati interfete specifice (mai multe) unei interfete generale (cu multe metode) un
API trebuie sa contina doar metode de care are nevoie clientul sau. Acest principiu este
crucial pentru (1. A scrie; 2. A mentine aplicatii) si biblioteci care se presupune ca o sa
creasca in timp. Este preferabil de a crea mai multe interfete separate decit combinarea
tuturor metodelor public intro clasa. Acest principiu poate fi privit ca un produs al
practicii TDD (deriven development). Segregarea cel mai des se intimpla in timpul
refactoringului.
D (DIP - Dependency inversion principle) abstractiile nu trebuie sa depinda de
implementarile concrete. Module de nivel nu trebuie sa depinda de modulele de nivel jos
si nu trebuie sa depinda de detalii (abstractiile nu trebuie sa depinda de abstractii),
detaliile trebuie sa depinda de abstractii, trebuie sa avem abstractii intermediare.

S- SRP (Single responsibility principle)

BAD CODE
Clasa Customer face lucruri care nu ar trebui sa le faca. Clasa ar trebui sa faca
validari de date a clientilor, sa accese layerul de acces a clientului, dar daca
observati, in blocul catch mai face activitatea de Logging (inscrie in fisier erorile). In
alte cuvinte, clasa este supraincarcata cu responsabilitati. Pe viitor, daca o sa se
adauge un eveniment de logger ca vizualizator de loguri, atunci ar trebui sa
schimbam clasa Customer, ceia ce nu este bine.
class Customer {
public void Add() {
try {
// Database code goes here
}
catch (Exception ex) {
System.IO.File.WriteAllText(@"c:\Error.txt", ex.ToString());
}
}
}
Principiul spune ca clasa trebuie sa aiba doar o singura responsabilitate. Deci ca
sa urmam principiul, noi vom separa activitate de logging in alta clasa, care o sa se
ocupe ea de logging

GOOD CODE
class FileLogger {
public void Handle(string error) {
System.IO.File.WriteAllText(@"c:\Error.txt", error);
}
}

class Customer {
private FileLogger obj = new FileLogger();
publicvirtual void Add() {
try {
// Database code goes here
}
catch (Exception ex) {
obj.Handle(ex.ToString());
}
}
}

Acum Clasa Customer poate delega activitate catre clasa FileLogger, si deci,
poate sa se concentreze la activitatile cu clientii dar nu si cu tratarea erorilor.
O- OCP (Open closed principle)

BAD CODE
Avem clasa Customer, in care avem o proprietate ce indica tipul clientului.
Proprietatea decide daca clientul este Gold sau Silver. In dependenta de
aceasta, ea calculeaza reducerea (in functia getDiscount).
class Customer {
private int _CustType;

public int CustType {


get { return _CustType; }
set { _CustType = value; }
}

public double getDiscount(double TotalSales) {


if (_CustType == 1) {
return TotalSales - 100;
}
else {
return TotalSales - 50;
}
}
}

Problema este in aceia, ca daca mai adaugam un tip de client, va fi nevoie de


adaugat inca o conditie IF in functia getDiscount, prin alte cuvinte, suntem nevoiti
de a schimba clasa Customer.
Daca vom schimba clasa din nou si din nou, atunci vom fi nevoiti de fiecare data
sa testam daca conditiile IF anterioare functioneaza corect. Cu alte cuvinte noi
Modificam codul pentru clasa Customer de fiecare data cind se adauga un tip nou
de customer, si ne trebuie sa ne asiguram ca celelate functionalitati functioneaza
asa cum si inainte.
GOOD CODE
Deci pentru a respeccta principiul, in loc sa Modificam noi voi Extinde clasa.
Cu alte cuvinte, de fiecare data cind vom avea nevoie de a adauga un alt tip de
consumator, noi vom crea alta clasa cum este aratat mai jos. Si ca rezultat, codul va
ramine neschimbat si usor de testat pentru viitor.
class Customer {
public virtual double getDiscount(double TotalSales) {
return TotalSales;
}
}
class SilverCustomer : Customer {
public override double getDiscount(double TotalSales) {
return base.getDiscount(TotalSales) - 50;
}
}
class goldCustomer : SilverCustomer {
public override double getDiscount(double TotalSales) {
return base.getDiscount(TotalSales) - 100;
}
}
Cu alte cuvinte, clasa a devenit inchisa pentru modificari, dar deschisa pentru
extindere.
L- LSP (Liskov substitution principle)

Clasa copil, nu trebuie sa distruga tipul clasei parintelui cit si comportamentul


ei. Deci, avem Clasa Employee ca clasa parinte pentru clase Casual Employee si
clasa Contractual Employee, care sunt mostenite de la Employee.

BAD CODE

public abstract class Employee {


public virtual string GetProjectDetails(int employeeId) {
return "Base Project";
}
public virtual string GetEmployeeDetails(int employeeId) {
return "Base Employee";
}
}
public class CasualEmployee : Employee {
public override string GetProjectDetails(int employeeId) {
return "Child Project";
}
//Posibil pentru contractual employee noi nu avem nevoie sa stocam
informatii in baza de date
public override string GetEmployeeDetails(int employeeId) {
return "Child Employee";
}
}
public class ContractualEmployee : Employee {
public override string GetProjectDetails(int employeeId) {
return "Child Project";
}
//Posibil pentru contractual employee noi nu avem nevoie sa stocam
informatii in baza de date
public override string GetEmployeeDetails(int employeeId) {
throw new NotImplementedException();
}
}
Pina acum, totul lucreaza bine, insa codul de mai jos va avea probleme:

List<Employee> employeeList = new List<Employee>();


employeeList.Add(new ContractualEmployee());
employeeList.Add(new CasualEmployee());
foreach (Employee e in employeeList)
{
e.GetEmployeeDetails(1245);
}
In urma rularii codului vom obtine o problema. Nu a fost tratata exceptia pentru
contractual Employee, deci din cauza asta se incalca principiul LSP. Problema poate
fi solutionata prin separarea in 2 interfete diferite, 1.IProject si 2.IEmployee care
implementeaza conform tipului lui employee

GOOD CODE

public interface IEmployee {


string GetEmployeeDetails(int employeeId);
}

public interface IProject {


string GetProjectDetails(int employeeId);
}
Acum Contractual Employee o sa implementeze IEmployee si nu IProject, aceasta va
mentine acest principiu.

I- ISP (Interface Segregation principle)

BAD CODE

Spre exemplu avem o clasa care deserveste 1000 de clienti, si toti din ei sunt
bucurosi ca o utilizeaza. Acum haideti sa adaugam clienti noi, care ne spun ca
doresc o metoda, care ajuta de a citi date despre alti clienti. Deci, programatorii
sunt foarte entuziasti si vor schimba interfata IDatabase asa cum in exemplul de
mai jos:

interface IDatabase {
void Add(); // clientii vechi sunt multumiti cu aceasta metoda
voidRead(); // a fost adaugata pentru clienti noi
}
Daca vom vizualiza cerintele care au fost adaugate, vom observa ca deja exista 2
tipuri de clienti:

Acei care doresc sa utilizeze NUMAI metoda Add()


Si alti care doresc sa utilizeze metode Add() + Read()

Deoarece am schimbat interfata curenta, noi impunem acei 1000 clienti satisfacuti,
sa utilizezer metoda Read.

GOOD CODE

Deci pentru a solutiona problema, este mai bine de a pastra acei 1000 clienti cu
interfata veche, si sa cream alta interfata pentru clienti noi. Pentru asta vom crea
interfata noua care actualizeaza interfata curenta. Se va pastra IDatabase asa
cum ea este, si se va adauga interfata IDatabaseV1 cu metoda adaugatoare
Read().

interface IDatabaseV1 : IDatabase // Gets the Add method


{
void Read();
}

Acum veti putea crea clase care implementeaza metoda Read() si satisfac clienti
noi, cit si cei vechi, deoarece ei au ramas neatinsi si sunt satisfacuti cu interfata lor
veche, care nu include metoda Read().

class CustomerwithRead : IDatabase, IDatabaseV1 {


public void Add() {
Customer obj = new Customer();
Obj.Add();
}
public void Read() {
// Implements logic for read
}
}
Deci clientii vechi continuie sa utilizeze interfata IDatabase, cit si cei noi utilizeaza
interfata IDatabaseV1.

IDatabase i = new Customer(); // 1000 happy old clients not touched


i.Add();

IDatabaseV1 iv1 = new CustomerWithread(); // new clients


Iv1.Read();
D- DIP (Dependency inversion principle)

BAD CODE
Sa presupunem ca avem un sistem de notificare care salveaza careva detalii in baza
de date

public class Email {


public void SendEmail() {
// cod pentru a trimete un email
}
}

public class Notification


{
private Email _email;
public Notification() {
_email = new Email();
}

public void PromotionalNotification() {


_email.SendEmail();
}
}

Acum clasa Notification total depinde de clasa Email, deoarece numai ea trimite
tipul notificarii. Daca noi dorim sa introducem alta de genul SMS, atunci ce ? Atunci
noi vom avea nevoie de a schimba si sistemul de notificare.

GOOD CODE

public interface IMessenger {


void SendMessage();
}
public class Email : IMessenger {
public void SendMessage() {
// code to send email
}
}
public class SMS : IMessenger {
public void SendMessage() {
// code to send SMS
}
}
public class Notification {
private IMessenger _iMessenger;
public Notification() {
_ iMessenger = new Email();
}
public void DoNotify() {
_ iMessenger.SendMessage();
}
}
Clasa Notification mai depinde de clasa Email. Acum putem folosi injection de
dependenta pentru a reduce legatura (schimbam clasa Notification).

public class Notification {


public void DoNotify(IMessenger pMessenger) {
pMessenger.SendMessage();
}
}

S-ar putea să vă placă și