본문 바로가기

IT Book Summary/Object: 객체지향설계

Appendix B 타입계층의 구현

타입 != 클래스

타입은 개념의 분류를 의미하고 클래스는 타입을 구현하는 한가지 방법일 뿐.

 

타입은 다양한 방법으로 구현가능

 

다양한 방식으로 구현된 타입들을 하나의 타입계층 안에 조합.

 


클래스를 이용한 타입 계층 구현 : 사용자 정의 타입 user-defined data type

 

ex) 10장의 Phone 클래스

Phone은 calculateFee 메시지에 응답할 수 있는 타입을 선언하는 동시에 객체 구현을 정의

 

퍼블릭 인터페이스는 동일하지만 다른 방식으로 구현해야 하는 객체가 필요하다면 상속을 이용

 

But 부모클래스와 강하게 결합되므로 가급적 추상클래스 상속과 인터페이스 구현하는 방법이 바람직

 

인터페이스를 이용한 타입 계층 구현

 

상속으로 인한 결합도 문제를 피하고 다중 상속이라는 구현 제약도 해결할 수 있는 방법

->클래스가 아닌 인터페이스를 사용

 

인터페이스를 이용해 타입의 퍼블릭 인터페이스를 정의하고 클래스를 이용해 객체를 구현하는 것이 일반적인 패턴

  • 여러 클래스가 동일한 타입을 구현가능 - 객체의 구현을 다를지라도 인터페이스는 같을 수 있다
  • 하나의 클래스가 여러 타입을 구현가능 

ex) 영화 예매시스템

 

클래스가 아니라 타입에 집중하자. 

타입을 중심으로 객체들의 계층을 설계하는 것.

 

추상클래스를 이용한 타입 계층 구현

 

내부 구현이 아닌 추상 메서드의 시그니처에만 의존

 

구체 메서드가 추상메서드를 호출하며 자식클래스들은 모두 이 추상 메서드의 시그니처를 준수

구체메서드가 추상메서드에 의존하기 때문에 의존성 역전원칙을 따른다 라고 할 수 있다.

이 설계는 유연하면서 변화에 안정적이다.

 

상속을 염두에 두고 설계해야 쉽게 확장 가능하고 결합도에 의한 부작용을 줄임.

 

추상클래스와 인터페이스 결합하기

 

인터페이스를 이용해 타입을 정의하고

특정 상속 계층에 국한된 코드를 공유 할 필요가 있을 경우 추상클래스를 이용해 코드 중복을 방지하는 것.

-> 골격 구현 추상 클래스 skeletal implementation abstract class

 

인터페이스는 여전히 타입을 정의하지만 골격 구현 클래스는 그것을 구현하는 모든 일을 맏는다

 

두가지 장점

  • 다양한 구현 방법이 필요할 경우 새로운 추상 클래스를 추가해서 쉽게 해결 가능.
  • 이미 부모 클래스가 존재하는 클래스라도 인터페이스를 추가함으로써 새로운 타입으로 쉽게 확장 가능

 

But 단일 상속 계층으로도 타입계층을 구현하는데 무리가 없다면

클래스나 추상클래스를 이용해 타입을 정의하는것이 더 나은방법.

 

덕 타이핑 사용하기

 

동적 타입 언어에서 지원하는 방식.

객체가 어떤 인터페이스에 정의된 행동을 수행할 수 있다면 그 객체를 해당 타입으로 분류해도 됨.

 

런타임에 타입을 결정하는 동적 타입 언어는 특정한 클래스를 상속받거나 인터페이스를 구현하지 않고도

객체가 수신할 수 있는 메시지의 집합으로 객체의 타입을 결정.

시그니처가 동일한 메서드를 가진 클래스는 같은 타입으로 취급

 

덕 타이핑은 클래스나 인터페이스에 대한 의존성을 메시지에 대한 의존성으로 대체

 

ex) C#의 dynamic 키워드를 사용하여 덕타이핑을 수행 가능

 

but 덕타이핑을 사용하면 설계가 유연하지마 컴파일 시점에서 발견할 수 잇는 오류를

실행시점으로 미루게 되어 코드의 안전성을 약화시킬수 있음.

 

ex) C++ 제네릭 프로그래밍 template 이용해 두 클래스를 동일한 타입으로 묶어 취급가능.

 

믹스인과 타입계층

 

믹스인 : 객체를 생성할 때 코드를 일부 섞에 넣을수 있도록 만들어진 일종의 추상 서브클래스.

공통의 행동이 믹스인된 객체들은 동일한 메시지를 수신할 수 있는 퍼블릭 인터페이스를 공유하게 되는것.

 

ex) 스칼라의 트레이트 trait

 

트레이트와 유사하게 자바8에서 추가된 default method는 인터페이스에 메서드의 기본 구현을 추가하는 것을 허용함.

인터페이스에 디폴트 메서드를 이용하면 더이상 구현을 위한 추상클래스가 필요하지 않음.

 

But

protected 로 추상클래스 안에 구현되었던 메서드가 public으로 인터페이스 내에서 오픈되어야 하는 문제.

외부에 노출할 필요가 없는 메서드를 불필요하게 인터페이스에 추가해야함.

인터페이스가 불필요하게 비대해질수 있음.

 

디폴트 메서드 의 진짜 역할 :  기본에 널리 사용되는 인터페이스에 새로운 오퍼레이션을 추가할 경우

발생하는 하위 호환성 문제를 해결하기 위해서임.

 

기본 구현을 포함하는 인터페이스를  정의하는 두가지 방법

  • 인터페이스 내부에 정적메서드를 사용
  • 인터페이스 기본구현을 제공 할 수 있게  디폴트 메서드를 사용

리스코프 치환 원칙을 준수하지 않는다면 올바른 타입 계층을 구현한것이 아님.

코드 재사용과 서브타이핑을 혼동하지 알자.