LSP(Liskov Substitution Principle) - 리스코프 치환 원칙
- 부모(super class)로 동작하는 곳에서 자식(sub class)를 넣어주어도 대체가 가능해야 한다는 원칙
- 자식 클래스를 구현할 때, 기본적으로 부모 클래스의 기능이나 능력들을 물려받는데 여기서 자식 클래스가 동작할 때, 부모 클래스의 기능들을 제한하면 안된다는 뜻
- 상속을 할 때 완전한 상속이 아닌 경우는 Interface로 구현하는 것이 맞다(Refactoring 책에서도 나옴)
- 자바의 stack이 부모의 기능을 모두 사용하지도 않으면서 array 상속받아 사용
- 부모 클래스의 타입에 자식 클래스의 인스턴스를 넣어도 똑같이 동작하여야 한다
BadCase
class RectangleBad {
var width: Float = 0
var height: Float = 0
var area: Float {
return width * height
}
}
class SquareBad: RectangleBad {
override var width: Float {
didSet {
height = width
}
}
}
func printArea(of rectangle: RectangleBad) {
rectangle.height = 3
rectangle.width = 6
print(rectangle.area)
}
let rectangle = RectangleBad()
printArea(of: rectangle)
let square = SquareBad()
printArea(of: square)
- Rectangle(부모) 클래스를 상속받은 Square(자식) 클래스에서 부모의 모든 자원을 활용하지 못하는 형태라 LSP에 어긋나게 된다.
- 부모의 역할을 자신이 온전하게 대체하지 못함
NiceCase
protocol Shape {
var area: Float { get }
}
class RectangleNice: Shape {
private let width: Float
private let height: Float
var area: Float {
return width * height
}
init(width: Float, height: Float) {
self.width = width
self.height = height
}
}
class SquareNice: Shape {
private let length: Float
var area: Float {
return self.length * self.length
}
init(length: Float) {
self.length = length
}
}
let rectangleNice = RectangleNice(width: 3, height: 6)
let squareNice = SquareNice(length: 6)
- Protocol을 사용하여 area 함수를 구현하게 만들어 Rectangle, Square 각각이 area 함수를 구현한다
- 구현부는 채택하는 하위 클래스로 넘기면 LSP의 원칙에 어긋나지 않는다