'오브젝트: 코드로 이해하는 객체지향 설계' 3~4주차
분량 : Ch.11 ~ Ch.15
기간 : 22.5.21 ~ 22.6.4
상속의 용도
부모 클래스는 일반적인 개념을 구현하고 자식 클래스는 특수한 개념을 구현한다.
부모 클래스는 자식 클래스의 일반화이고, 자식 클래스는 부모 클래스의 특수화이다.
상속을 사용하면 간단하게 코드를 재사용할 수 있지만, 결합도가 강하기 때문에 변경하기 어려운 코드를 얻게 될 확률이 높다.
-> 자세한 내용은 10장에서 정리했다.
상속을 사용하는 일차적인 목표는 타입 계층을 구현하는 것이다.
Ch.13에서는 올바른 타입 계층을 구성하는 원칙에 대해 알아본다.
객체지향 패러다임의 관점
객체지향 프로그래밍에서 오퍼레이션은 객체가 수신할 수 있는 메시지를 의미함
객체의 타입이란 객체가 수신할 수 있는 메시지의 종류를 정의하는 것
객체가 수신할 수 있는 메시지의 집합을 가리키는 용어 : 퍼블릭 인터페이스
즉, 동일한 퍼블릭 인터페이스를 가지는 객체들은 동일한 타입으로 분류할 수 잇다.
타입 계층을 표현할 때는 더 일반적인 타입을 위쪽에, 더 특수한 타입을 아래쪽에 배치하는 것이 관례다.
타입 계층을 구성하는 두 타입 간의 관계에서 더 일반적인 타입을 슈퍼타임(supertype),
더 특수한 타입을 서브타입(subtype)이라고 부른다.
일반화는 다른 타입을 완전힣 포함하거나 내포하는 타입을 식별하는 행위 또는 그 행위의 결과를 가리킨다.
특수화는 다른 타입 안에 전체적으로 포함되거나 완전히 내포되는 타입을 식별하는 행위 또는 그 행위의 결과를 가리킨다.
서브타입의 인스턴스는 슈퍼타입의 인스턴스로 간주될 수 있다.
-> 상속과 다형성의 관계를 이해하기 위한 출발점
마틴 오더스키는 다음 두 질문에 모두 '예'라고 답할 수 있는 경우에만 상속을 사용하라고 조언한다.
상속 관계가 is-a 관계를 모델링하는가?
->"[자식 클래스]는 [부모 클래스]다" 라고 말해도 이상하지 않다면 상속을 사용할 후보로 간주할 수 있다.
클라이언트 입장에서 부모 클래스의 타입으로 자식 클래스를 사용해도 무방한가?
-> 클라이언트의 입장에서 부모 클래스와 자식 클래스의 차이점을 몰라야 한다. (자식 클래스와 부모 클래스 사이의 행동 호환성이라고 부름)
-> 첫 번째 질문보다는 두 번째 질문에 초점을 맞추는 것이 중요하다.
펭귄은 새지만 만약 새의 정의에 날 수 있다는 행동이 포함된다면 펭귄은 새의 서브타입이 될 수 없다. 만약 새의 정의에 날 수 있다는 행동이 포함되지 않으면 펭귄은 새의 서브타입이 될 수 있다.
타입 계층의 의미는 행동이라는 문맥에 따라 달라질 수 있다.
따라서 is-a 관계에 성립한다고 해도, 그저 후보로 생각해야 하는 것이다.
타입의 이름 사이에 개념적으로 어떤 연관성이 있다고 하더라도 행동에 연관성이 없다면 is-a 관계를 사용하지 말아야 한다.
펭귄이 새의 서브타입이 아닌 이유는 클라이언트 입장에서 모든 새가 날 수 있다고 가정하기 때문이다.
클라이언트의 기대에 맞게 상속 계층을 분리하는 것
날 수 있는 새와 날 수 없는 새를 분리한다.
public class Bird {
...
}
public class FlyingBird extends Bird {
public void fly() { ... }
}
public class Penguin extends Bird {
...
}
// 날 수 있는 새한테만 보낸다.
public void flyBird(FlyingBird bird) {
bird.fly();
}
만약 날 수 없는 새에게는 걸으라는 메시지를 보내려면 어떻게 해야 할까?
-> fly 오퍼레이션을 가진 Flyer 인터페이스와 walk 오퍼레이션을 가진 Walker 인터페이스로 분리할 수 있다.
만약 Penguin이 Bird의 코드를 재사용해야 한다면?
-> 합성으로 해결할 수 있다.
클라이언트의 기대에 따라 인터페이스를 분리함으로써 변경에 의해 영향을 제어하는 설계 원칙을 인터페이스 분리 원칙(Interface Segregation Principle, ISP)라고 한다.
상속을 사용하는 두 가지 목적에 따라 두 가지로 나뉜다.
-> 서브클래싱과 서브타이핑
서브타입은 그것의 기반 타입에 대해 대체 가능해야 한다.
클라이언트가 차이점을 인식하지 못한 채 기반 클래스의 인터페이스를 통해 서브클래스를 사용할 수 있어야 한다.
클라이언트의 입장에서 퍼블릭 인터페이스의 행동 방식이 변경되지 않는다면 클라이언트의 코드를 변경하지 않고도 새로운 자식 클래스와 협력할 수 있게 된다는 것이다.
리스코프 치환 원칙은 개방-폐쇄 원칙을 만족하는 설계를 위한 전제 조건이다.
리스코프 치환 원칙 위반 = 잠ㅐ적인 개방-폐쇄 원칙 위반
리스코프 치환 원칙을 따르는 설는 유연할뿐만 아니라 확장성이 높다.