A class should have ****only one reason to change—it should do only one job or have one responsibility.
Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. It minimizes changes to existing, tested code.
Tightly Coupled Code
type Shape struct {
shapeType string
}
func (s Shape) Draw() {
if s.shapeType == "circle" {
fmt.Println("Drawing a Circle")
} else if s.shapeType == "square" {
fmt.Println("Drawing a Square")
}
}
Extensible Code
New shapes can be added without modifying existing code
type Shape interface {
Draw()
}
type Circle struct{}
func (c Circle) Draw() {
fmt.Println("Drawing a Circle")
}
type Square struct{}
func (s Square) Draw() {
fmt.Println("Drawing a Square")
}
The LSP ensures that a subclass enhance or modify the base class behavior in a way that doesn't violate client expectations.
Violating LSP (Breaking Expected Behavior)
A Bird base class or a Flyer interface that includes a Fly method suggests that all subclasses or implementers should be able to fly.
type Bird struct{}
func (b Bird) Fly() string {
return "I can fly!"
}
type Penguin struct {
Bird
}
func (p Penguin) Fly() string {
return "I can't fly!"
}
type Flyer interface {
Fly() string
}
type Bird struct{}
func (b Bird) Fly() string {
return "I can fly!"
}
type Penguin struct {}
func (p Penguin) Fly() string {
return "I can't fly!"
}
Complying LSP
Split the behaviors into distinct interfaces.
type Flyer interface {
Fly() string
}
type Walker interface {
Walk() string
}
type Bird struct{}
func (b Bird) Fly() string {
return "I can fly!"
}
type Penguin struct {}
func (p Penguin) Walk() string {
return "I can walk!"
}