////
Search

13장 - 서브클래싱과 서브타이핑

Date
2024/04/07 13:07
Tags

1. 개요

상속의 용도는 다음과 같다.
타입 계층 구현 = 타입계층 안에서 부모클래스는 일반적인 개념을 구현하고 자식 클래스는 특수한 개념을 구현한다.
부모 클래스는 자식 클래스의 일반화(generalization)
자식 클래스는 부모 클래스의 특수화(specialization)
코드 재사용
간단한 선언만으로 부모 클래스의 코드를 재사용할 수 있다.
재사용을 위해 상속을 사용하는 경우 부모 클래스와 자식 클래스가 강하게 결합되어 변경하기 어려운 코드를 얻게 될 확률이 높다.

2. 타입

2.1. 개념 관점의 타입

개념 관점에서 타입이란 우리가 인지하는 세상의 사물의 종류를 의미한다.
타입 : 타입은 사물을 분류하기 위한 틀로 사용된다.
인스턴스 : 어떤 대상이 타입으로 분류될 때의 대상을 가리켜 인스턴스라고 부른다.
객체 : 일반적으로 타입의 인스턴스를 가리켜 객체라고 부른다.
심볼 : 타입에 이름을 붙인 것.
내연(intension) : 타입의 정의로서 타입에 속하는 객체들이 가지는 공통적인 속성이나 행동
예를 들어 프로그래밍 언어의 정의인 컴퓨터에게 특정한 작업을 지시하기 위한 어휘와 문법적 규칙의 집합 이 바로 내연이다.
외연(extension) : 타입에 속하는 객체들의 집합
예를들어 프로그래밍 언어 타입의 경우에는 자바, 루비, 자바스크립트, C가 속한 집합이 외연을 구성한다.

2.2. 프로그래밍 언어 관점의 타입

프로그래밍 언어 관점에서의 타입은 연속적인 비트의 의미와 제약을 부여하기 위해 사용된다.
비트에 담긴 데이터를 문자열로 다룰지 정수로 다룰지는 전적으로 데이터를 사용하는 애플리케이션에 의해 결정된다.
따라서 프로그래밍 언어의 관점에서 타입은 비트 묶음에 의미를 부여하기 위해 정의된 제약과 규칙을 가리킨다.
프로그래밍 언어에서 타입은 두 가지 목적을 위해 사용된다.
타입에 수행될 수 있는 유효한 오퍼레이션의 집합을 정의한다.
타입에 수행되는 오퍼레이션에 대해 미리 약속된 문맥을 제공한다.

2.3. 객체지향 패러다임 관점의 타입

지금까지의 내용을 바탕으로 타입을 다음과 같은 두 가지 관점에서 정의할 수 있다.
개념 관점에서 타입이란 공통의 특징을 공유하는 대상들의 분류다.
프로그래밍 언어 관점에서 타입이란 동일한 오퍼레이션을 적용할 수 있는 인스턴스들의 집합이다.
객체지향 프로그래밍에서 오퍼레이션은 객체가 수신할 수 있는 메시지를 의미한다.
따라서 객체의 타입이란 객체가 수신할 수 있는 메시지의 종류를 정의하는 것이다.
객체가 수신할 수 있는 메시지를 기준으로 타입을 분류하기 때문에 동일한 퍼블릭 인터페이스를 가지는 객체들은 동일한 타입으로 분류할 수 있다.

3. 타입 계층

3.1. 타입 사이의 포함관계

수학에서 집합은 다른 집합을 포함할 수 있다.
타입 역시 객체들의 집합이기 때문에 다른 타입을 포함하는 것이 가능하다
타입 안에 포함된 객체들은 좀 더 상세한 기준으로 묶어 새로운 타입을 정의하면 이 새로운 타입은 자연스럽게 기존 타입의 부분 집합이 된다.
타입 계층을 표현할 때는 더 일반적인 타입을 위쪽에 더 특수한 타입을 아래쪽에 배치하는 것이 관례다.
슈퍼타입 : 타입계층을 구성하는 두 타입 간의 관계에서 더 일반적인 타입
서브타입 : 타입계층을 구성하는 두 타입 간의 관계에서 더 특수한 타입
내연과 외연의 관점에서 일반화와 특수화를 정의해보자
내연 관점
일반화 = 어떤 타입의 정의를 좀 더 보편적이고 추상적으로 만드는 과정을 의미한다.
특수화 : 어떤 타입의 정의를 좀 더 구체적이고 문맥 종속적으로 만드는 과정
외연 관점
일반화 : 특수한 타입의 인스턴스 집합을 표현하는 슈퍼셋(superset)
특수화 : 일반적인 타입의 인스턴스 집합에 포함된 서브셋(subset)

3.2. 객체지향 프로그래밍과 타입 계층

객체의 타입을 결정하는 것은 퍼블릭 인터페이스다
일반적인 타입이란 비교하려는 타입에 속한 객체들의 퍼블릭 인터페이스보다 더 일반적인 퍼블릭 인터페이스를 가지는 객체들의 타입을 의미한다.
퍼블릭 인터페이스의 관점에서 슈퍼타입과 서브타입을 다음과 같이 정의할 수 있다.
슈퍼타입 이란 서브타입이 정의한 퍼블릭 인터페이스를 일반화 시켜 상대적으로 범용적이고 넓은 의미 로 정의한 것이다.
서브타입 이란 슈퍼타입이 정의한 퍼블릭 인터페이스를 특수화 시켜 상대적으로 범용적이고 좁은 의미 로 정의한 것이다.

4. 서브클래싱과 서브타이핑

4.1. 언제 상속을 사용해야 하는가?

올바른 용도는 타입 계층을 구현하는 것
아래 질문에 모두 yes라고 답할 수 있을때만 상속을 사용한다.
상속관계가 is-a 관계를 모델링하는가?
일반적으로 자식 클래스는 부모 클래스 라고 말해도 이상하지 않다면 상속을 사용할 후보로 간주할 수 있다.
클라이언트 입장에서 부모 클래스의 타입으로 자식 클래스를 사용해도 무방한가?
상속 계층을 사용하는 클라이언트 입장에서 부모 클래스와 자식 클래스의 차이점을 몰라야 한다.
이를 자식 클래스와 부모 클래스 사이의 행동 호환성 이라 부른다.

4.2. is-a 관계

두 클래스가 어휘적으로 is-a 관계를 모델링할 경우에만 상속을 사용해야 한다.
하지만 is-a 관계가 직관적이고 명확하지는 않다.
다음의 예는 is-a 관계가 직관적이지 않음을 보여주는 예시이다.
타입 계층의 의미는 행동이라는 문맥에 따라 달라질 수 있다.
따라서 슈퍼타입과 서브타입 관계에서는 is-a보다 행동 호환성이 더 중요하다

4.3. 행동 호환성

타입 사이에 개념적으로 어떤 연관성이 있다고 하더라도 행동에 연관성이 없다면 is-a 관계를 사용하지 말아야 한다.
결론은 두 타입 사이에 행동이 호환되는 경우에만 타입 계층으로 묶어야 한다.
여기서 중요한 것은 행동의 호환 여부를 판단하는 기준은 클라이언트 관점이다.
클라이언트가 두 타입이 동일하게 행동할 것이라고 기대한다면 두 타입을 타입 계층으로 묶을 수 있다.
하지만 클라이언트가 두 타입이 동일하게 행동하지 않을 것이라고 기대한다면 두 타입을 타입 계층으로 묶어서는 안된다.

4.4. 클라이언트의 기대에 따라 계층 분리하기

클라이언트 기대에 따라 상속계층 분리 시
인터페이스 분리 원칙 = 인터페이스를 클라이언트 기대에 따라 분리함으로써 변경에 영향을 제어하는 설계원칙

4.5. 서브클래싱과 서브타이핑

서브클래싱(subclassing) : 다른 클래스의 코드를 재사용할 목적으로 상속을 사용하는 경우
자식 클래스와 부모 클래스의 행동이 호환되지 않기 때문에 자식 클래스의 인스턴스가 부모 클래스의 인스턴스를 대체할 수 없다.
구현상속(implementation inheritance) 또는 클래스 상속 (class inheritance)이라고 부른다
서브타이핑(subtyping) : 타입 계층을 구성하기 위해 상속을 사용하는 경우
자식클래스와 부모 클래스의 행동이 호환되기 때문에 자식 클래스의 인스턴스가 부모 클래스의 인스턴스를 대체할 수 있다.
서브타이핑을 인터페이스 상속 (interface inheritance) 이라고 한다
서브타입이 슈퍼타입이 하는 모든 행동을 동일하게 할 수 있어야 한다
행동 호환성을 만족 시켜야 한다.
서브클래싱과 서브타이핑을 나누는 기준은 상속을 사용하는 목적이다.
자식이 부모의 코드를 재사용할 목적 = 서브 클래싱
부모 대신 자식 인스턴스를 사용할 목적 = 서브 타이핑
서브 타이핑 관계가 유지되기 위해선 슈퍼타입이 하는 모든 행동을 동일하게 할 수 있어야 한다.
어떤 타입이 다른 타입의 서브타입이 되지 위해선 행동 호환성을 만족시켜야함
자식 / 부모 클래스 사이의 행동 호환성은 부모 클래스에 대한 자식 클래스의 대체 가능성을 포함한다.

5. 리스코프 치환원칙

리스코프 치완 원칙
서브 타입은 그것의 기반 타입에 대해 대체 가능해야 한다는 것
클라이언트가 "차이점을 인식하지 못한 채 파생 클래스의 인터페이스를 통해 서브 클래스를 사용할 수 있어야 한다"는 것이다.

6. 계약에 의한 설계와 서브타이핑

계약에 의한 설계 (Design By Contract, DBC)
클라이언트와 서버 사이의 협력을 의무와 이익으로 구성된 계약의 관점에서 표현하는 것
계약에 의한 설계의 세 가지 요소로 구성된다.
사전조건(precondition) : 클라이언트가 정상적으로 메서드를 실행하기 위해 만족시켜야 하는 조건
사후조건(postcondition) : 메서드가 실행된 후에 클라이언트에게 보장해야 하는 조건
클래스 불변식(class invariant) : 메서드 실행 전과 실행 후에 인스턴스가 만족시켜야 하는 조건
리스코프 치환 원칙과 계약에 의한 설계 사이의 관계를 한 문장으로 요약하자면 다음과 같다.
서브타입이 리스코프 치환 원칙을 만족시키기 위해서는 클라이언트와 슈퍼타입 간에 체결된 계약 을 준수해야한다.