Sobes.tech
Junior

De ce răspunde litera 'L' în principiile SOLID?

sobes.tech AI

Răspuns de la AI

Principiul de substituție a lui Barbara Liskov (Principiul de substituție Liskov).

Conform acestui principiu, obiectele dintr-un program trebuie să poată fi înlocuite cu instanțe ale subtipurilor lor fără a modifica corectitudinea programului.

Cu alte cuvinte, dacă clasa B este un subtip al clasei A, atunci orice obiect de tip A din program poate fi înlocuit cu un obiect de tip B, iar programul va continua să funcționeze corect.

Acest principiu ajută la crearea unei arhitecturi mai flexibile și rezistente la schimbări, minimizând efectele secundare atunci când se lucrează cu moștenirea.

Încălcarea LSP:

class Rectangle {
    var width: Double
    var height: Double

    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }

    var area: Double {
        return width * height
    }
}

class Square: Rectangle { // Patratul este un subtip al dreptunghiului
    override var width: Double {
        didSet {
            height = width // Schimbarea lățimii schimbă și înălțimea
        }
    }

    override var height: Double {
        didSet {
            width = height // Schimbarea înălțimii schimbă și lățimea
        }
    }

    init(side: Double) {
        super.init(width: side, height: side)
    }
}

func calculateArea(of rectangle: Rectangle) -> Double {
    return rectangle.area
}

let myRectangle = Rectangle(width: 5, height: 10)
print(calculateArea(of: myRectangle)) // 50

let mySquare = Square(side: 5)
// Presupunem că acesta este un dreptunghi 5x5 cu suprafața 25.
// Dar, atunci când îl folosim într-o funcție care așteaptă un Rectangle,
// comportamentul său poate fi neașteptat dacă se schimbă lățimea sau înălțimea
// înainte de apelarea calculateArea, deoarece Square schimbă ambele părți simultan.
// În acest exemplu, dacă nu se schimbă proprietățile după inițializare,
// rezultatul va fi 25, dar dacă, de exemplu, se face mySquare.width = 10
// înainte de apelarea calculateArea, aria sa va deveni 100, ceea ce poate
// fi neașteptat pentru codul care lucrează cu Rectangle.
print(calculateArea(of: mySquare)) // 25

În exemplu, Square încalcă LSP, deoarece schimbarea unei dimensiuni (width sau height) schimbă și cealaltă. Dacă folosim un obiect Square acolo unde se aștepta un Rectangle, acest lucru poate duce la comportamente neașteptate, deoarece codul care lucrează cu Rectangle poate presupune posibilitatea de a modifica părțile în mod independent.

Conformitatea cu LSP:

Pentru a respecta LSP în acest caz, se poate împărți ierarhia sau se poate folosi compoziția în loc de moștenire pentru Square. De exemplu, se poate introduce o interfață sau un protocol comun pentru formele care au suprafață și se poate implementa în Rectangle și Square.

protocol Shape {
    var area: Double { get }
}

class Rectangle: Shape {
    var width: Double
    var height: Double

    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }

    var area: Double {
        return width * height
    }
}

class Square: Shape {
    var side: Double

    init(side: Double) {
        self.side = side
    }

    var area: Double {
        return side * side
    }
}

func calculateShapeArea(of shape: Shape) -> Double {
    return shape.area
}

let myShapeRectangle: Shape = Rectangle(width: 5, height: 10)
print(calculateShapeArea(of: myShapeRectangle)) // 50

let myShapeSquare: Shape = Square(side: 5)
print(calculateShapeArea(of: myShapeSquare)) // 25

Acum, Square și Rectangle implementează un protocol comun Shape, și putem folosi obiecte ale ambelor clase acolo unde se așteaptă un Shape, fără a încălca comportamentul așteptat.