Назад к вопросам
Junior
93
questionbank

За что отвечает буква 'L' в принципах SOLID?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Принцип замещения Барбары Лисков (Liskov Substitution Principle).

Согласно этому принципу, объекты в программе должны быть заменяемыми на экземпляры их подтипов без изменения правильности выполнения программы.

Иными словами, если класс B является подтипом класса A, тогда любой объект типа A в программе может быть заменен на объект типа B, и при этом программа продолжит работать корректно.

Этот принцип помогает создавать более гибкую и устойчивую к изменениям архитектуру, минимизируя побочные эффекты при работе с наследованием.

Нарушение 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 { // Квадрат является подтипом прямоугольника
    override var width: Double {
        didSet {
            height = width // Изменение ширины также меняет высоту
        }
    }

    override var height: Double {
        didSet {
            width = height // Изменение высоты также меняет ширину
        }
    }

    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)
// Предполагаем, что это прямоугольник 5x5 с площадью 25.
// Но при использовании в функции, ожидающей Rectangle,
// его поведение может быть непредвиденным, если изменить ширину или высоту
// до вызова calculateArea, так как Square меняет обе стороны одновременно.
// В данном примере, если не менять свойства после инициализации,
// результат будет 25, но если, например, сделать mySquare.width = 10
// перед вызовом calculateArea, его площадь станет 100, что может
// быть неожидаемо для кода, работающего с Rectangle.
print(calculateArea(of: mySquare)) // 25

В примере Square нарушает LSP, так как изменение одного измерения (width или height) также меняет другое. Если мы используем объект Square там, где ожидался Rectangle, это может привести к неожиданному поведению, так как код, работающий с Rectangle, может предполагать возможность независимого изменения сторон.

Соответствие LSP:

Чтобы соблюсти LSP в данном случае, можно разделить иерархию или использовать композицию вместо наследования для Square. Например, ввести общий интерфейс или протокол для фигур, которые имеют площадь, и реализовать его в Rectangle и 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

Теперь Square и Rectangle реализуют общий протокол Shape, и мы можем использовать объекты обоих классов там, где ожидается Shape, без нарушения ожидаемого поведения.