A cosa si riferisce la lettera 'L' nei principi SOLID?
sobes.tech AI
Risposta dell'AI
Principio di sostituzione di Barbara Liskov (**Principio di sostituzione di Liskov).
Secondo questo principio, gli oggetti in un programma devono essere sostituibili con istanze dei loro sottotipi senza modificare la correttezza del programma.
In altre parole, se la classe B è un sottotipo della classe A, allora qualsiasi oggetto di tipo A nel programma può essere sostituito da un oggetto di tipo B, e il programma continuerà a funzionare correttamente.
Questo principio aiuta a creare un'architettura più flessibile e resistente ai cambiamenti, minimizzando gli effetti collaterali quando si lavora con l'ereditarietà.
Violazione del 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 { // Il quadrato è un sottotipo di rettangolo
override var width: Double {
didSet {
height = width // Cambiare la larghezza cambia anche l'altezza
}
}
override var height: Double {
didSet {
width = height // Cambiare l'altezza cambia anche la larghezza
}
}
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)
// Si presume che sia un rettangolo 5x5 con area 25.
// Ma quando viene usato in una funzione che si aspetta un Rectangle,
// il suo comportamento può essere imprevisto se si modificano larghezza o altezza
// prima di chiamare calculateArea, poiché Square cambia entrambi i lati contemporaneamente.
// In questo esempio, se non si modificano le proprietà dopo l'inizializzazione,
// il risultato sarà 25, ma se, ad esempio, si imposta mySquare.width = 10
// prima di chiamare calculateArea, la sua area diventerà 100, il che può
// essere inaspettato per il codice che lavora con Rectangle.
print(calculateArea(of: mySquare)) // 25
Nell'esempio, Square viola il LSP perché cambiare una dimensione (width o height) cambia anche l'altra. Se usiamo un oggetto Square dove ci si aspetta un Rectangle, questo può portare a comportamenti imprevisti, poiché il codice che lavora con Rectangle può presumere la possibilità di modificare i lati in modo indipendente.
Conformità al LSP:
Per rispettare il LSP in questo caso, si può dividere la gerarchia o usare la composizione invece dell'ereditarietà per Square. Ad esempio, introdurre un'interfaccia o protocollo comune per le figure che hanno area, e implementarlo in 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
Ora, Square e Rectangle implementano un protocollo comune Shape, e possiamo usare oggetti di entrambe le classi dove ci si aspetta un Shape, senza violare il comportamento atteso.