OOP
[오브젝트] 13장 - 서브 클래싱 및 서브 타이핑(2)
Donee
2024. 7. 21. 00:08
LSP
- 서브 타입과 서브 클래스가 부모 클래스, 상위 타입 대체가 가능 해야함
- 불가능한 예시
- 직사각형, 정사각형
- 현실에서는 정사각형은 직사각형의 한 종류 지만, 동작이 달라 상속이 불가능 함
- why? 정사각형은 높이를 변경하면, 너비도 함께 바뀜(
- 따라서, 두 사각형의 높이를 변경했을 때 넓이의 변화가 다름(직사각형은 높이만큼, 정사각형은 변경된 높이의 제곱만큼 영향을 받음)
- 따라서 클라이언트의 가정에 따라 부모 클래스 가정을 세우고 맞게 처리해야 함
- is-a 관계는 클라의 입장에서 고려, 자식 객체가 부모 객체의 행동을 대체할 수 있어야 함(속성은 필요X)
LSP의 효과
- 유연한 설계(어떤 자식 클래스과도 안정적으로 협력이 가능)
- OCP를 위한 전제 조건
- why? 위반시엔 자식 클래스를 추가할 때마다 문제가 발생하기 때문
계약에 의한 설계
- 서브 타입은, LSP 만족을 위해 클라이언트 ↔ 슈퍼타입 간 체결된 계약을 준수
- 계약에는 사전조건, 사후조건, 클래스 불변식
- 사전 조건은 클라이언트가 정상적 메서드 실행하기 위해 만족시켜야 함
- 사후 조건은 메서드 실행 후, 서버가 클라에게 보장해야 함
- 두 조건을 만족하지 못하면, 협력에 문제가 발생
사전 조건 위반 예시
- 서브 타입은 더 강력한 사전 조건을 정의할 수 없음
- 만약 정의하게 된다면 다음과 같은 문제점 발생
class Example {
private var calculator: Calculator
private let number: Int
func calculate() -> Int? {
if number >= 0 {
return calculator.execute(value: number)
}
return nil
}
}
- Example 객체는, Calculator 객체와 협력
- Calculator 객체는 전달받은 number를 특별한 규칙에 의해 계산 후, 값을 전달(이때, 음수를 전달받으면 음수를 에러가 발생함)
- Example은 양수외에는 nil을 리턴하는 구조
- 이때, Calculator 의 사전 조건은 양수만을 전달할 것임
class ScientificCalculator: Calculator {
func execute(value: Int) -> Int {
assert(value >= 10, "Value must be 10 or greater")
}
}
- ScientificCalculator 라는 Calculator 의 파생 클래스
- 해당 객체는, 양수중에서도 10이상의 숫자만 들어와야 함
- 하지만, 협력객체인 Example 은 이걸 알수 없어, 에러가 계속 발생
- 결국 서브 타입 의 사전 조건이 부모 타입보다 더 컸을 때 위험
사후 조건 위반 사례
- 서브타입은 슈퍼타입과 같거나 더 강한 사후조건 정의 가능
- 더 약한 사후조건은 정의할 수 없음
- 위 Example객체는 Calculator 가 항상 양수 이상의 값을 전달한다고 생각
class OfficeCalculator: Calcuator {
func execute(value: Int) -> Int {
return value * -1
}
}
- 위와같이, OfficeCalcualtor 에서 음수를 전달한다면, 협력 객체인 Example 에서는 에러가 발생
- 위 사례는, 서브타입이 슈퍼타입 보다 더 약한 사후조건을 정의하여 문제가 발생
결론
- LSP 즉 서브 타입이 슈퍼 타입을 대체할 수 있어야 함
- 서브 타입은 객체 협력간 사전 조건, 사후 조건을 만족시켜야 함
- 서브 타이핑을 원한다면, 슈퍼 타입의 계약관계 및 클라이언트의 가정을 잘 숙지해야 함