Sobes.tech
Junior

Za čo je zodpovedné písmeno 'L' v princípoch SOLID?

sobes.tech AI

Odpoveď od AI

Princíp zámenny Liskov (Liskovov princíp zámenny).

Podľa tohto princípu by objekty v programe mali byť nahraditeľné inštanciami svojich podtypov bez zmeny správnosti programu.

Inými slovami, ak trieda B je podtriedou triedy A, potom akýkoľvek objekt typu A v programe môže byť nahradený objektom typu B, a program bude naďalej správne fungovať.

Tento princíp pomáha vytvárať flexibilnejšiu a odolnejšiu architektúru, minimalizujúc vedľajšie efekty pri práci s dedičnosťou.

Porušenie 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 { // Štvorec je podtriedou obdĺžnika
    override var width: Double {
        didSet {
            height = width // Zmena šírky tiež mení výšku
        }
    }

    override var height: Double {
        didSet {
            width = height // Zmena výšky tiež mení šírku
        }
    }

    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)
// Predpokladáme, že je to obdĺžnik 5x5 s plochou 25.
// Ale pri použití vo funkcii, ktorá očakáva Rectangle,
// jeho správanie môže byť nepredvídateľné, ak zmeníme šírku alebo výšku
// pred volaním calculateArea, pretože Square mení obe strany súčasne.
// V tomto príklade, ak nezmeníme vlastnosti po inicializácii,
// výsledok bude 25, ale ak napríklad nastavíme mySquare.width = 10
// jeho plocha bude 100, čo môže byť neočakávané pre kód pracujúci s Rectangle.
print(calculateArea(of: mySquare)) // 25

V príklade Square porušuje LSP, pretože zmena jednej dimenzie (width alebo height) tiež mení druhú. Ak používame objekt Square tam, kde sa očakáva Rectangle, môže to viesť k neočakávanému správaniu, pretože kód pracujúci s Rectangle môže predpokladať možnosť nezávislej zmeny strán.

Dodržiavanie LSP:

Aby sme dodržali LSP v tomto prípade, môžeme hierarchiu rozdeliť alebo použiť kompozíciu namiesto dedičstva pre Square. Napríklad zaviesť spoločné rozhranie alebo protokol pre tvary, ktoré majú plochu, a implementovať 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

Teraz Square a Rectangle implementujú spoločný protokol Shape, a môžeme používať objekty oboch tried tam, kde sa očakáva Shape, bez narušenia očakávaného správania.