Introduction
Robert C. Martin coined the mnomonic acronym SOLID to represent 5 principles of software design. This is the third in my own five-part series exploring my understanding of and experience applying these principles:
- [SRP] The Single-Responsibility Principle:
- "Software entities should have only one reason to change."
- [OCP] The Open–Closed Principle:
- "Software entities should be 'open for extension but closed for modification'."
- [LSP] The Liskov Substitution Principle: (you are here)
- "Subtypes must be substitutable for their base types."
- [ISP] The Interface Segregation Principle:
- "Clients should not be forced to depend on methods they do not use."
- [DIP] The Dependency Inversion Principle:
- "High-level policy should depend upon abstractions, not concretions."
LSP
This principle is mostly concerned with the potential for voliating invariants when using inheritance to define a heirachy. The example below is in Go, which doesn't really have traditional inheritance, but does support a concept called embedding, which is simliar:
type Rectangle struct {
TopLeft Point
Length int
Width int
}
type Square struct {
Rectangle
}
In this example, the Square
struct embeds the Rectangle
. This is probably because the developer who wrote this code was used to thinking of a square being a special case of rectangle, which can often indicate a good candidate for sub-classing and inheritance. 'Under the hood', there's an actual field on the Square
of type Rectangle
, but you can access the Rectangle
fields (and methods, if any are defined) as if they were defined on the Square
.
func main() {
s := Square{
Rectangle: Rectangle{
TopLeft: Point{0, 0},
Length: 4,
Width: 4,
},
}
fmt.Println(s.Length, s.Width) // Output: 4 4
}
But the embedding of Rectangle to represent a Square is...not good:
func main() {
s := Square{
Rectangle: Rectangle{
TopLeft: Point{0, 0},
Length: 4,
Width: 4,
},
}
s.Length = 5
fmt.Println(s.Length, s.Width) // Output: 5 4
}
Well, that's awkward, a word which here means a violation of LSP.
Next Section: The Interface Segragation Principle