출처 : 디자인 패턴에 뛰어들기 - 알렉산더 슈베츠 도서
L (Liskov Substitution Principle, 리스코프 치환 원칙)
클래스를 확장할 때, 자식 클래스의 객체들을 부모 클래스의 객체들로 교체(치환)할 수 있어야 합니다. 이는 자식 클래스가 부모 클래스의 행동과 호환되어야 함을 의미한다.
목적
자식 클래스가 부모 클래스와 호환되도록 하여 기존 클라이언트 코드가 자식 클래스와도 잘 작동하게 하는 것이다.
특히 라이브러리와 프레임워크 개발 시 중요하다.
규칙
메서드 매개변수의 유형
- 자식 클래스의 메서드 매개변수는 부모 클래스의 것보다 더 추상적이거나 같은 수준이어야 한다.
- 예시:
- 좋은 코드: feed(Animal c)는 feed(Cat c)를 오버라이드하여 모든 동물에게 먹이를 줄 수 있다.
- 나쁜 코드: feed(BengalCat c)는 특정 고양이 품종으로만 제한한다.
메서드 반환 유형
- 자식 클래스의 메서드 반환 유형은 부모 클래스의 반환 유형의 하위 유형이거나 일치해야 한다.
- 예시:
- 좋은 코드: buyCat(): BengalCat는 buyCat(): Cat을 오버라이드하여 벵갈 고양이를 반환한다.
- 나쁜 코드: buyCat(): Animal은 예상치 못한 동물을 반환한다.
예외 처리
- 자식 클래스의 메서드는 부모 클래스 메서드에서 예상되지 않는 예외를 던지면 안된다.
- 예시:
- 부모 클래스 메서드가 던질 수 있는 예외 유형들만 던져야 클라이언트 코드가 예외를 처리할 수 있다.
- 부모 클래스 메서드가 던질 수 있는 예외 유형들만 던져야 클라이언트 코드가 예외를 처리할 수 있다.
사전 조건 강화 금지
- 자식 클래스는 부모 클래스 메서드의 사전 조건을 강화하면 안된다.
- 예시:
- 부모 메서드가 음수 값을 허용하는데, 자식 메서드가 이를 허용하지 않으면 클라이언트 코드가 망가질 수 있다.
- 부모 메서드가 음수 값을 허용하는데, 자식 메서드가 이를 허용하지 않으면 클라이언트 코드가 망가질 수 있다.
사후 조건 약화 금지
- 자식 클래스는 부모 클래스 메서드의 사후 조건을 약화하면 안된다.
- 예시:
- 부모 메서드가 데이터베이스 연결을 닫는다면, 자식 메서드도 동일해야 한다.
- 부모 메서드가 데이터베이스 연결을 닫는다면, 자식 메서드도 동일해야 한다.
불변성 보존
- 부모 클래스의 불변성을 자식 클래스에서도 유지해야 한다.
- 예시:
- 고양이의 불변성은 다리 4개, 꼬리가 있음, 야옹할 수 있음 등이 있다.
- 고양이의 불변성은 다리 4개, 꼬리가 있음, 야옹할 수 있음 등이 있다.
비공개 필드 변경 금지
- 자식 클래스는 부모 클래스의 비공개 필드를 변경해서는 안된다.
- 일부 프로그래밍 언어는 반사 메커니즘을 통해 클래스의 비공개 멤버들에 접근할 수 있지만, 이는 권장되지 않는다.
예시
리스코프 치환 원칙을 위반하는 문서 클래스들의 계층구조에 대한 예시이다.
수정 전: 저장하기 기능은 읽기 전용 문서에서는 의미가 없으므로 자식 클래스는 이 문제를 오버라이드된 메서드에서 기초 행동을 재설정하여 해결하려고 한다.
ReadOnlyDocuments 클래스의 자식 클래스에서는 save() 메서드를 호출하면 예외가 발생한다.
그러나 기본 클래스에는 이러한 제한이 없습니다. 이것은 문서의 유형을 확인하지 않고 문서를 저장하려고 할 때 클라이언트 코드가 충돌할 수 있음을 의미하고 있다.
또한, 결과 코드는 개방/폐쇄 원칙을 위반한다.
이는 클라이언트 코드가 문서들의 구상 클래스에 의존하여 새로운 문서 자식 클래스를 도입할 때 클라이언트 코드를 변경해야 한다는 것을 의미하고 있다.
수정 후: 읽기 전용 문서의 클래스를 계층 구조의 기초 클래스로 만들어 문제가 해결되었다.
이 문제를 해결하기 위해 클래스 계층 구조를 재설계할 수 있다.
여기서 자식 클래스는 부모 클래스의 행동을 확장해야 한다.
따라서 읽기 전용 문서가 계층 구조의 기본 클래스로 설정된다.
그리고 쓰기 가능한 문서는 이제 기본 클래스를 확장하고 "저장"이라는 행동을 추가하는 자식 클래스가 된다.
결론
상속 관계에 있는 클래스들은 서브 타입을 부모 타입으로 대체할 수 있어야 한다는 것을 의미한다.
즉, 부모 클래스의 인스턴스를 대체하여 사용할 때 프로그램의 의도에 어긋나지 않아야 한다.
이를 위해 서브 클래스는 부모 클래스의 기능을 확장하거나 변경하지 않고, 기존의 동작을 유지해야 한다.
이러한 원칙을 준수하면 코드의 안정성을 유지하고 확장성을 향상시킬 수 있다.
'Self-Dev > Design Patterns R&D' 카테고리의 다른 글
[12] 소프트웨어 디자인 원칙들 - SOLID 원칙들 - (D) (0) | 2024.06.02 |
---|---|
[11] 소프트웨어 디자인 원칙들 - SOLID 원칙들 - (I) (2) | 2024.06.02 |
[9] 소프트웨어 디자인 원칙들 - SOLID 원칙들 - (O) (0) | 2024.06.01 |
[8] 소프트웨어 디자인 원칙들 - SOLID 원칙들 - (S) (0) | 2024.06.01 |
[7] 소프트웨어 디자인 원칙들 - 디자인 원칙들 (2) | 2024.06.01 |