Sobes.tech
Junior

Wofür ist der Buchstabe 'L' in den SOLID-Prinzipien verantwortlich?

sobes.tech KI

Antwort von AI

Prinzip der Liskov-Substitution (Liskov-Substitutionsprinzip).

Nach diesem Prinzip sollten Objekte in einem Programm durch Instanzen ihrer Untertypen ersetzt werden können, ohne die Korrektheit des Programms zu verändern.

Mit anderen Worten, wenn die Klasse B ein Untertyp der Klasse A ist, kann jedes Objekt vom Typ A im Programm durch ein Objekt vom Typ B ersetzt werden, und das Programm funktioniert weiterhin korrekt.

Dieses Prinzip hilft dabei, eine flexiblere und widerstandsfähigere Architektur zu schaffen, indem Nebenwirkungen bei der Arbeit mit Vererbung minimiert werden.

Verletzung des 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 { // Das Quadrat ist ein Untertyp des Rechtecks
    override var width: Double {
        didSet {
            height = width // Änderung der Breite ändert auch die Höhe
        }
    }

    override var height: Double {
        didSet {
            width = height // Änderung der Höhe ändert auch die Breite
        }
    }

    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)
// Es wird angenommen, dass dies ein Rechteck 5x5 mit Fläche 25 ist.
// Bei Verwendung in einer Funktion, die ein Rectangle erwartet,
// kann sein Verhalten unerwartet sein, wenn Breite oder Höhe vor dem Aufruf von calculateArea geändert werden,
// da Square beide Seiten gleichzeitig ändert.
// In diesem Beispiel, wenn die Eigenschaften nach der Initialisierung nicht geändert werden,
// ist das Ergebnis 25, aber wenn z.B. mySquare.width auf 10 gesetzt wird
// vor dem Aufruf von calculateArea, wird die Fläche 100, was
// unerwartet sein kann für den Code, der mit Rectangle arbeitet.
print(calculateArea(of: mySquare)) // 25

Im Beispiel verletzt Square den LSP, da die Änderung einer Dimension (width oder height) auch die andere ändert. Wenn ein Objekt Square dort verwendet wird, wo ein Rectangle erwartet wird, kann dies zu unerwartetem Verhalten führen, da der Code, der mit Rectangle arbeitet, die Möglichkeit unabhängiger Änderungen der Seiten annimmt.

Einhaltung des LSP:

Um das LSP in diesem Fall einzuhalten, kann die Hierarchie aufgeteilt oder Komposition anstelle von Vererbung für Square verwendet werden. Zum Beispiel kann eine gemeinsame Schnittstelle oder ein Protokoll für Figuren eingeführt werden, die eine Fläche haben, und in Rectangle und Square implementiert werden.

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

Jetzt implementieren Square und Rectangle ein gemeinsames Protokoll Shape, und wir können Objekte beider Klassen dort verwenden, wo ein Shape erwartet wird, ohne das erwartete Verhalten zu verletzen.