Za co odpowiada litera 'L' w zasadach SOLID?
sobes.tech AI
Odpowiedź od AI
Zasada podstawiania Liskov (Zasada podstawiania Liskov).
Zgodnie z tą zasadą, obiekty w programie powinny być wymienialne na instancje ich podtypów bez zmiany poprawności działania programu.
Inaczej mówiąc, jeśli klasa B jest podtypem klasy A, to dowolny obiekt typu A w programie może być zastąpiony obiektem typu B, a program będzie nadal działał poprawnie.
Ta zasada pomaga tworzyć bardziej elastyczną i odporną na zmiany architekturę, minimalizując skutki uboczne podczas pracy z dziedziczeniem.
Naruszenie 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 { // Kwadrat jest podtypem prostokąta
override var width: Double {
didSet {
height = width // Zmiana szerokości również zmienia wysokość
}
}
override var height: Double {
didSet {
width = height // Zmiana wysokości również zmienia szerokość
}
}
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)
// Zakładamy, że to prostokąt 5x5 z polem 25.
// Jednak podczas używania w funkcji oczekującej Rectangle,
// jego zachowanie może być nieprzewidywalne, jeśli zmieni się szerokość lub wysokość
// przed wywołaniem calculateArea, ponieważ Square zmienia obie strony jednocześnie.
// W tym przykładzie, jeśli nie zmienimy właściwości po inicjalizacji,
// wynik będzie 25, ale jeśli np. ustawimy mySquare.width = 10
// przed wywołaniem calculateArea, jego pole stanie się 100, co może
// być nieoczekiwane dla kodu pracującego z Rectangle.
print(calculateArea(of: mySquare)) // 25
W przykładzie Square narusza LSP, ponieważ zmiana jednego wymiaru (width lub height) również zmienia drugi. Jeśli używamy obiektu Square tam, gdzie oczekiwano Rectangle, może to prowadzić do nieoczekiwanego zachowania, ponieważ kod pracujący z Rectangle może zakładać możliwość niezależnej zmiany boków.
Zgodność z LSP:
Aby spełnić LSP w tym przypadku, można podzielić hierarchię lub użyć kompozycji zamiast dziedziczenia dla Square. Na przykład, wprowadzić wspólny interfejs lub protokół dla figur, które mają pole, i zaimplementować go w Rectangle i 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
Teraz Square i Rectangle implementują wspólny protokół Shape, i możemy używać obiektów obu klas tam, gdzie oczekiwany jest Shape, bez naruszania oczekiwanego zachowania.