- 객체 합성의 의미는 public 상속이 가진 의미와 완전히 다르다.
- 응용 영역에서 객체 합성의 의미는 has-a이다. 구현 영역에서는 is-implemented-in-terms-of의 의미이다.
has-a, is-implemented-in-terms-of 객체 합성
어떤 타입의 객체들이 그와 다른 타입의 객체들을 포함하는 경우를 합성이라고 한다.
class Address { ... };
class phoneNumber { ... };
class Person {
public:
...
private:
std::string name;
Address address;
PhoneNumber voiceNumber;
PhoneNumber faxNumber;
};
Person객체는 string, Address, PhoneNumber 객체로 이루어져 있다.
합성 말고도 레이어링, 포함, 통합, 내장 등으로도 불린다.
public 상속의 의미는 is-a이다.
객체 합성의 의미는 has-a와 is-implemented-in-terms-of의 뜻이 있다.
객체 중에는 일상생활에서 볼 수 있는 사물을 본뜬 것들이 있다. 사람, 이동수단, 비디오 프레임 등 이런 객체는 소프트웨어의 응용 영역에 속한다.
응용 영역에 속하지 않는 나머지들은 버퍼, 뮤텍스, 탐색 트리 등 시스템 구현만을 위한 객체이다.
이런 종류의 객체가 속한 부분은 소프트웨어의 구현 영역이라고 한다.
객체 합성이 응용 영역의 객체들 사이에서 일어나면 has-a 관계이다. 반면, 구현 영역에서 일어나면 is-implemented-in-terms-of 관계를 나타낸다.
위의 예제에서 Person 클래스가 나타내는 관계는 has-a 관계이다.
Person 객체는 이름, 주소, 전화번호를 가지고 있다. 이러한 경우 is-a 관계와 has-a 관계의 역할을 헷갈리는 경우는 별로 없다.
하지만 is-a 관계와 is-implemeted-in-terms-of 관계는 헷갈릴 수 있다.
객체로 구성된 작은 집합을 나타내고 저장 공간도 적게 차지하는 클래스의 템플릿이 필요하다고 가정해 보자.
std::set을 이용하려 구현하려 하는데 std::set은 균형 탐색 트리로 구현되어 있어 원소 한 개당 포인터 세 개의 오버헤드가 걸리도록 구현되어 있다.
그래서 std::set을 사용하는 것은 적절하지 않은 상황이다.
따라서 연결 리스트로 구현하려 한다.
즉, Set<T>는 list<T>로부터 상속을 받는다.
template<typename T>
class Set: public std::list<T> { ... };
is-a 관계에서는 B에서 참인 것들이 전부 D에서도 참이어야 한다.
하지만 list 객체는 중복 원소를 가질 수 있는 컨테이너이다. 하지만, Set 객체는 원소가 중복되면 안 된다.
따라서 Set과 list는 is-a관계가 아니다.
그렇기 때문에 public 상속은 지금 상황과 맞지 않는다.
하지만, Set 객체는 list 객체를 써서 구현하는 형태의 설계가 가능하다.
template<class T>
class Set {
public:
bool member(const T& item) const;
void insert(const T& item);
void remove(const T& item);
std::size_t size() const;
private:
std::list<T> rep; // Set 데이터의 내부 표현부
};
Set의 멤버 함수는 list에서 이미 제공하는 기능 및 표준 C++ 라이브러리의 다른 구성 요소를 활용하면 된다.
template<typename T>
bool Set<T>::member(const T& item) const
{
return std::find(rep.begin(), rep.end(), item) != rep.end();
}
template<typename T>
void Set<T>::insert(const T& item)
{
if(!member(item)) rep.push_back(item);
}
template<typename T>
void Set<T>::remove(const T& item)
{
typename std::list<T>::iterator it = std::find(rep.begin(), rep.end(), item);
if(it != rep.end()) rep.erase(it);
}
template<typename T>
std::stize_t Set<T>::size() const
{
return rep.size();
}