서브클래싱과 서브타이핑에 대한 내용이다
자세한 내용이 궁금하면 오브젝트를 펼쳐보는 걸 추천드립니다.📖
시작하면서
동일한 메시지에 대해 서로 다르게 행동할 수 있는 다형적인 객체를 구현하기 위해서는 객체의 행동을 기반으로 타입 계층을 구성해야 한다. 타입 사이의 관계를 고려하지 않은 채 코드 재사용을 위해 상속하면 여러 문제를 갖게 된다.
개념 관점의 타입
우리가 인지하는 세상의 사물의 종류를 개념 관점의 타입이라 하며, 어떤 대상이 타입으로 분류 될 때 그 대상을 타입의 '인스턴스(instance)'라고 부른다. 일반적으로 타입의 인스턴스를 '객체'라고 부른다.
타입은 세 가지 요소로 구성된다
- 심볼(symbol)이란 타입에 이름을 붙인 것이다.
- 내연(intension)이란 타입에 속하는 객체들이 공유하는 속성과 행동의 집합이다.
- 외연(extension)이란 타입에 속하는 객체들의 집합이다.
프로그래밍 언어 관점의 타입
비트 묶음에 의미를 부여하기 위해 정의된 제약과 규칙을 프로그래밍 언어 관점의 타입이며, 두 가지 목적으로 사용된다
- 타입에 수행될 수 있는 유효한 오퍼레이션의 집합을 정의한다.
- 타입에 수행되는 오퍼레이션에 대해 미리 약속된 문맥을 제공한다.
객체지향 패러다임 관점의 타입
위의 두 관점을 정리하자면 객체의 타입이란 객체가 수신할 수 있는 메시지의 종류를 정의하는 것이다. 이건 바로 퍼블릭 인터페이스다. 객체지향 프로그래밍에서 타입을 정의하는 것은 객체의 퍼블릭 인터페이스를 정의하는 것과 동일하다.
객체의 퍼블릭 인터페이스가 객체의 타입을 결정한다. 따라서 동일한 퍼블릭 인터페이스를 제공하는 객체들은 동일한 타입으로 간주한다.
타입 사이의 포함관계
집합은 다른 집합을 포함할 수 있다. 타입도 객체들의 집합이기 때문에 다른 타입을 포함하는 게 가능하다. 타입 계층을 표현할 때에는 일반적인 타입을 위쪽에, 더 특수한 타입은 아래쪽에 배치하는 것이 관례다.
타입 계층을 구성하는 관계에서 일반적인 타입은 슈퍼타입(supertype)이라 부르고 더 특수한 타입을 서브타입(subtype)이라고 부른다.
내연의 관점에서 일반화란 어떤 타입의 정의를 좀 더 추상적으로 만드는 과정이고,
특수화는 어떤 타입의 정의를 좀 더 구체적으로 만든다.
외연의 관점에서 일반적인 타입의 인스턴스 집합은 특수한 타입의 인스턴스 집합을 포함하는 슈퍼셋(superset)이고,
특수한 타입의 인스턴스 집합은 일반적인 타입의 인스턴스 집합에 포함된 서브셋(subset)이다.
일반화는 다른 타입을 완전히 포함하거나 내포하는 타입을 식별하는 행위 또는 그 행위의 결과
특수화는 다른 타입 안에 전체적으로 포함되거나 완전히 내포되는 타입을 식별하는 행위 또는 그 행위의 결과
객체지향 프로그래밍과 타입 계층
슈퍼타입이란 서브타입이 정의한 퍼블릭 인터페이스를 일반화시켜 상대적으로 범용적이고 넓은 의미로 정의한 것이다.
서브타입이란 슈퍼타입이 정의한 퍼블릭 인터페이스를 특수화시켜 상대적으로 구체적이고 좁은 의미로 정의한 것이다.
상속 사용 조건
- 상속 관계가 is-a 관계를 모델링하는가? (개념과 행동 모두 연관되어야 한다.)
- 클라이언트 입장에서 부모 클래스의 타입으로 자식 클래스를 사용해도 무방한가?
서브클래싱(subclassing)
다른 클래스의 코드를 재사용할 목적으로 상속하는 경우로 구현 상속 혹은 클래스 상속이라 부른다.
서브타이핑(subtyping)
타입 계층을 구성하기 위해 상속하는 경우로 인터페이스 상속이라고 부른다.
리스코프 치환 원칙(Liskov Substitution Principle, LSP)
리스코프 치환 원칙은 서브타입은 그것의 기반 타입에 대해 대체 가능해야 한다는 것으로 차이점을 인식하지 못한 채 기반 클래스의 인터페이스를 통해 서브클래스를 사용할 수 있어야 한다는 것이다.
원칙에 따르면 자식 클래스가 부모 클래스와 행동 호환성을 유지함으로써 부모 클래스를 대체할 수 있도록 구현된 상속 관계만을 서브타이핑이라고 불러야 한다.
클라이언트와 대체 가능성
리스코프 치환 원칙은 자식 클래스가 부모 클래스를 대체하기 위해서는 부모 클래스에 대한 클라이언트의 가정을 준수해야 하는 것을 강조한다.
상속 관계는 클라이언트의 관점에서 자식 클래스가 부모 클래스를 대체할 수 있을 때만 올바르다.
한마디로 대체 가능성을 결정하는 것은 클라이언트다.
리스코프 치환 원칙은 유연한 설계의 기반이다
LSP는 클라이언트가 어떤 자식 클래스와도 협력하도록 상속 구조를 구현할 수 있는 가이드라인을 제공한다. 즉, 클라이언트의 코드를 변경하지 않고도 새로운 자식 클래스와 협력할 수 있게 된다는 것이다.
계약에 의한 설계와 서브타이핑
클라이언트와 서버 사이의 협력을 의무(obligation)와 이익(benefit)으로 구성된 계약의 관점에서 표현하는 것을 계약에 의한 설계라고 부르며 세 가지 요소로 구성된다.
- 사전조건(precondition) : 클라이언트가 정상적으로 메서드를 실행하기 위해 만족시켜야 하는 조건
- 사후조건(postcondition) : 메서드가 실행된 후에 서버가 클라이언트에게 보장해야 하는 조건
- 클래스 불변식(class invariant) : 메서드 실행 전과 실행 후에 인스턴스가 만족시켜야 하는 조건
서브타입이 리스코프 치환 원칙을 만족시키기 위해서는 클라이언트와 슈퍼타입 간에 체결된 '계약'을 준수해야 한다.
'서적 > 오브젝트: 코드로 이해하는 객체지향 설계' 카테고리의 다른 글
오브젝트 15_ 디자인 패턴과 프레임워크 (0) | 2021.06.10 |
---|---|
오브젝트 14_ 일관성 있는 협력 (0) | 2021.06.10 |
오브젝트 12_ 다형성 (0) | 2021.06.10 |
오브젝트 11_ 합성과 유연한 설계 (0) | 2021.06.10 |
오브젝트 10_ 상속과 코드 재사용 (0) | 2021.06.09 |