Sobes.tech
Junior

De quoi la lettre 'L' est-elle responsable dans les principes SOLID?

sobes.tech IA

Réponse de l'IA

Principe de substitution de Barbara Liskov (**Principe de substitution de Liskov).

Selon ce principe, les objets dans un programme doivent être remplaçables par des instances de leurs sous-types sans modifier la correction du programme.

En d'autres termes, si la classe B est un sous-type de la classe A, alors tout objet de type A dans le programme peut être remplacé par un objet de type B, et le programme continuera à fonctionner correctement.

Ce principe aide à créer une architecture plus flexible et résistante aux changements, en minimisant les effets secondaires lors de l'utilisation de l'héritage.

Violation du 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 { // Le carré est un sous-type du rectangle
    override var width: Double {
        didSet {
            height = width // Changer la largeur change aussi la hauteur
        }
    }

    override var height: Double {
        didSet {
            width = height // Changer la hauteur change aussi la largeur
        }
    }

    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)
// On suppose que c'est un rectangle 5x5 avec une surface de 25.
// Mais lors de son utilisation dans une fonction qui attend un Rectangle,
// son comportement peut être inattendu si on modifie la largeur ou la hauteur
// avant d'appeler calculateArea, car Square modifie les deux côtés simultanément.
// Dans cet exemple, si on ne modifie pas les propriétés après l'initialisation,
// le résultat sera 25, mais si, par exemple, on fait mySquare.width = 10
// avant d'appeler calculateArea, sa surface deviendra 100, ce qui peut
// être inattendu pour le code qui travaille avec Rectangle.
print(calculateArea(of: mySquare)) // 25

Dans l'exemple, Square viole le LSP car changer une dimension (width ou height) change également l'autre. Si on utilise un objet Square là où on attend un Rectangle, cela peut conduire à un comportement inattendu, car le code qui travaille avec Rectangle peut supposer la possibilité de changer indépendamment les côtés.

Conformité au LSP:

Pour respecter le LSP dans ce cas, on peut diviser la hiérarchie ou utiliser la composition au lieu de l'héritage pour Square. Par exemple, introduire une interface ou un protocole commun pour les figures ayant une surface, et l'implémenter dans Rectangle et 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

Maintenant, Square et Rectangle implémentent un protocole commun Shape, et nous pouvons utiliser des objets des deux classes là où un Shape est attendu, sans violer le comportement attendu.