Sobes.tech
Junior

Za co odpovídá písmeno 'L' v principech SOLID?

sobes.tech AI

Odpověď od AI

Princip Liskovovy substituce (Liskovovo pravidlo substituce).

Podle tohoto pravidla by objekty v programu měly být nahraditelné instancemi svých podtříd bez změny správnosti programu.

Jinými slovy, pokud třída B je podtřídou třídy A, pak jakýkoli objekt typu A v programu může být nahrazen objektem typu B, a program bude nadále správně fungovat.

Toto pravidlo pomáhá vytvářet flexibilnější a odolnější architekturu, minimalizuje vedlejší efekty při práci s dědičností.

Porušení 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 { // Čtverec je podtřídou obdélníku
    override var width: Double {
        didSet {
            height = width // Změna šířky také mění výšku
        }
    }

    override var height: Double {
        didSet {
            width = height // Změna výšky také mění šířku
        }
    }

    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)
// Předpokládáme, že je to obdélník 5x5 s plochou 25.
// Ale při použití ve funkci, která očekává Rectangle,
// může být jeho chování nepředvídatelné, pokud změníme šířku nebo výšku
// před voláním calculateArea, protože Square mění obě strany současně.
// V tomto příkladu, pokud nezměníme vlastnosti po inicializaci,
// bude výsledek 25, ale pokud například nastavíme mySquare.width = 10
// před voláním calculateArea, jeho plocha bude 100, což může být neočekávané.
print(calculateArea(of: mySquare)) // 25

V příkladu Square porušuje LSP, protože změna jednoho rozměru (width nebo height) také změní druhý. Pokud použijeme objekt Square tam, kde se očekává Rectangle, může dojít k neočekávanému chování, protože kód pracující s Rectangle může předpokládat možnost nezávislé změny stran.

Dodržení LSP:

Abychom dodrželi LSP v tomto případě, můžeme hierarchii rozdělit nebo místo dědičnosti použít kompozici pro Square. Například zavést společné rozhraní nebo protokol pro tvary, které mají plochu, a implementovat ho v Rectangle a 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

Nyní Square a Rectangle implementují společný protokol Shape, a můžeme používat objekty obou tříd tam, kde je očekáván Shape, bez narušení očekávaného chování.