Sobes.tech
Junior

Do que é responsável a letra 'L' nos princípios SOLID?

sobes.tech IA

Resposta da IA

Princípio de substituição de Barbara Liskov (**Princípio de Substituição de Liskov).

De acordo com este princípio, os objetos num programa devem ser substituíveis por instâncias dos seus subtipos sem alterar a correção do programa.

Em outras palavras, se a classe B é um subtipo da classe A, então qualquer objeto do tipo A no programa pode ser substituído por um objeto do tipo B, e o programa continuará a funcionar corretamente.

Este princípio ajuda a criar uma arquitetura mais flexível e resistente a mudanças, minimizando efeitos secundários ao trabalhar com herança.

Violação do 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 { // O quadrado é um subtipo de retângulo
    override var width: Double {
        didSet {
            height = width // Alterar a largura também altera a altura
        }
    }

    override var height: Double {
        didSet {
            width = height // Alterar a altura também altera a largura
        }
    }

    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)
// Assume-se que é um retângulo 5x5 com área 25.
// Mas ao usá-lo numa função que espera um Rectangle,
// o seu comportamento pode ser inesperado se alterar a largura ou a altura
// antes de chamar calculateArea, pois Square altera ambos os lados ao mesmo tempo.
// Neste exemplo, se não alterar as propriedades após a inicialização,
// o resultado será 25, mas se, por exemplo, fizer mySquare.width = 10
// antes de chamar calculateArea, a sua área será 100, o que pode
// ser inesperado para o código que trabalha com Rectangle.
print(calculateArea(of: mySquare)) // 25

No exemplo, Square viola o LSP porque alterar uma dimensão (width ou height) também altera a outra. Se usarmos um objeto Square onde se esperava um Rectangle, isso pode levar a comportamentos inesperados, pois o código que trabalha com Rectangle pode assumir a possibilidade de alterar os lados de forma independente.

Conformidade com o LSP:

Para cumprir o LSP neste caso, pode-se dividir a hierarquia ou usar composição em vez de herança para Square. Por exemplo, introduzir uma interface ou protocolo comum para figuras que tenham área, e implementá-lo em Rectangle e 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

Agora, Square e Rectangle implementam um protocolo comum Shape, e podemos usar objetos de ambas as classes onde se espera um Shape, sem violar o comportamento esperado.