Sobes.tech
Junior

Waarvoor staat de letter 'L' in de SOLID-principes?

sobes.tech AI

Antwoord van AI

Principe van substitutie van Barbara Liskov (Liskov Substitutie Principe).

Volgens dit principe moeten objecten in een programma vervangbaar zijn door instanties van hun subtypen zonder de correctheid van het programma te wijzigen.

Met andere woorden, als klasse B een subtype is van klasse A, dan kan elk object van type A in het programma worden vervangen door een object van type B, en het programma blijft correct functioneren.

Dit principe helpt bij het creëren van een flexibelere en robuustere architectuur, waarbij bijwerkingseffecten bij het werken met erfelijkheid worden geminimaliseerd.

Overtreding van 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 { // De vierkant is een subtype van rechthoek
    override var width: Double {
        didSet {
            height = width // Wijzigen van breedte wijzigt ook de hoogte
        }
    }

    override var height: Double {
        didSet {
            width = height // Wijzigen van hoogte wijzigt ook de breedte
        }
    }

    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)
// We gaan ervan uit dat dit een rechthoek is van 5x5 met een oppervlakte van 25.
// Maar bij gebruik in een functie die een Rectangle verwacht,
// kan het gedrag onverwacht zijn als de breedte of hoogte wordt gewijzigd
// vóór het aanroepen van calculateArea, omdat Square beide zijden tegelijk wijzigt.
// In dit voorbeeld, als de eigenschappen niet worden gewijzigd na initialisatie,
// zal het resultaat 25 zijn, maar als bijvoorbeeld mySquare.width = 10 wordt gedaan
// vóór het aanroepen van calculateArea, wordt de oppervlakte 100, wat
// onverwacht kan zijn voor code die met Rectangle werkt.
print(calculateArea(of: mySquare)) // 25

In het voorbeeld schendt Square de LSP omdat het wijzigen van één maat (width of height) ook de andere wijzigt. Als we een Square object gebruiken waar een Rectangle werd verwacht, kan dit leiden tot onverwacht gedrag, omdat de code die met Rectangle werkt, mogelijk de mogelijkheid van onafhankelijke wijziging van zijden aanneemt.

Naleving van LSP:

Om LSP in dit geval te respecteren, kan de hiërarchie worden opgesplitst of compositie in plaats van erfelijkheid worden gebruikt voor Square. Bijvoorbeeld, een gemeenschappelijke interface of protocol voor vormen die oppervlakte hebben, introduceren en implementeren in Rectangle en 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

Nu implementeren Square en Rectangle een gemeenschappelijk protocol Shape, en kunnen we objecten van beide klassen gebruiken waar een Shape wordt verwacht, zonder het verwachte gedrag te schenden.