Sobes.tech
Junior

Mit felel az 'L' betű a SOLID elvekben?

sobes.tech MI

Válasz az MI-től

Liskov-helyettesítési elv (Liskov-helyettesítési elv).

Ezen elv szerint a programban lévő objektumokat helyettesíteni kell az azok altípusainak példányaival anélkül, hogy a program helyessége megváltozna.

Más szóval, ha az B osztály az A osztály altípusa, akkor az A típusú objektumot a programban B típusúra lehet cserélni, és a program továbbra is helyesen fog működni.

Ez az elv segít rugalmasabb és változásokkal szemben ellenállóbb architektúrák kialakításában, minimalizálva az öröklés során fellépő mellékhatásokat.

LSP megsértése:

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 { // A négyzet az téglalap altípusa
    override var width: Double {
        didSet {
            height = width // A szélesség változása a magasságot is változtatja
        }
    }

    override var height: Double {
        didSet {
            width = height // A magasság változása a szélességet is változtatja
        }
    }

    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)
// Feltételezzük, hogy ez egy 5x5-ös négyzet, területe 25.
// De a `Rectangle`-t váró függvényben való használatakor
// viselkedése lehet váratlan, ha a szélességet vagy magasságot megváltoztatjuk
// a `calculateArea` hívása előtt, mivel a Square mindkét oldalt egyszerre változtatja.
// Ebben a példában, ha nem változtatunk a tulajdonságokon az inicializálás után,
// az eredmény 25 lesz, de például, ha mySquare.width = 10-et írunk,
// a területe 100 lesz, ami váratlan lehet a Rectangle-t kezelő kód számára.
print(calculateArea(of: mySquare)) // 25

A példában a Square megsérti az LSP-t, mivel egy dimenzió (width vagy height) változtatása a másikat is változtatja. Ha olyan objektumot használunk Square-ből, ahol Rectangle-t várnak, az váratlan viselkedést eredményezhet, mivel a Rectangle-t kezelő kód feltételezheti, hogy a oldalakat függetlenül lehet változtatni.

LSP betartása:

Ebben az esetben az LSP betartásához szét lehet választani a hierarchiát, vagy kompozíciót használni az öröklés helyett a Square-nál. Például, közös interfész vagy protokoll bevezetése a területtel rendelkező alakzatok számára, és ennek megvalósítása Rectangle-ben és Square-ben.

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

Most már a Square és Rectangle megvalósítja a Shape protokollt, és az objektumaikat ott használhatjuk, ahol Shape-et várnak, anélkül, hogy a várt viselkedést megsértenénk.