티스토리 뷰

728x90

모형화할 때는 객체 합성을 사용하자

합성(composition)이란,
어떤 타입의 객체들이 그와 다른 타입의 객체들을 포함하고 있을 경우에 성립하는 그 타입들 사이의 관계를 일컫습니다.

포함된 객체들을 모아서 이들을 포함한 다른 객체를 합성한다는 뜻인데, 다음과 같은 경우입니다.

 

 

문제점

 
1
2
3
4
5
6
7
8
9
10
11
12
13
class Address { …. };     // 누군가의 거주지
class PhoneNumber { …. };
class Person
{
public:
 
private:
   std::string mane;  // 이 클래스를 이루는 객체 중 하나
   Address address;   // 마찬가지
   PhoneNumber voiceNumber;  // 역시 마찬가지
   PhoneNumber faxNumber;  // 이것도 마찬가지
};
 
cs

 

Person 객체는 string, Address와 PhoneNumber 객체로 이루어져 있습니다.

이런 경우를 두고 합성이라는 말을 사용하는데 다른 말로는 레이어링(layering), 포함(containment), 통합(addregetion), 내장(embedding)이라고도 합니다.

항목 32에서 public 상속의 의미가 “is – a(.. 는 … 의 일종이다)”라고 배웠습니다.

객체 합성 역시 의미를 갖고 있습니다.

실제 뜻이 2개나 되는데 2개인 이유는 소프트웨어 개발에서 대응하는 영역이 두 가지이기 때문입니다.

사물을 본뜬 사람, 이동수단, 비디오 프레임 등인데, 이런 객체는 소프트웨어의 응용 영역(application domain)에 속하고,

나머지들은 버퍼, 뮤 텍스, 탐색 트리 등 순수하게 시스템 구현만을 위한 인공물입니다.

이런 종류의 객체가 구현 영역(implementation domain)이라고 합니다.

여기서 객체 합성이 응용 영역의 객체들 사이에서 일어나면 has –a 관계입니다.

반면, 구현 영역에서 일어나면 is-implemented – in – terms – of 관계를 나타낸 것입니다.

 

위의 person 클래스는 has –a 관계입니다.

사람이 이름의 일종, 사람이 주소의 일종이라고는 말할 수 없는 것입니다.

그러나 사람이 이름을 가지며 사람이 주소를 가진다고 하는 것은 자연스럽습니다.

이렇기 때문에 is - a관계와 has - a관계의 역할을 헷갈리는 경우는 없습니다.

 

헷갈리는 부분은 is - a관계와 is – implemented – in – terms – of 관계의 차이점 일 것입니다.

한 예로, 객체로 구성된 작은 집합, 중복 원소가 없는 집합체를 나타내고 저장 공간도 적게 차치하는 클래스 템플릿이 필요하다고 했을 때,

표준 라이브러리의 set템플릿을 활용해 보기로 합니다.

set템플릿은 원소 한 개당 포인터 세 개의 오버헤드가 걸리도록 구현되어 있습니다.

그러므로 표준 라이브러리의 set템플릿은 적당하지 않습니다.

이번엔 list를 사용해보겠습니다.

이번 포인트는 set템플릿을 만들되 list에서 파생된 형태부터 시작되도록 만든다는 것입니다.

set <T>는 list <T>로부터 상속을 받습니다.

1
2
3
4
5
6
7
8
 
// set을 만든답시고 list를 잘못 쓰는 방법
template<typename T>
class set : public std::list<T> 
{
//… 
};
 
cs

 

항목 32의 설명에 따르면 D와 B 사이에 is - a관계가 성립하면 B에서 참인 것들이 전부 D에서도 참이어야 합니다.
하지만 list 객체는 중복 원소를 가질 수 있는 컨테이너입니다.
구현하고 싶은 건 중복된 원소가 있어서는 안 되는 겁니다.
이들 두 클래스 사이의 관계가 is – a가 될 리 없으므로, public 상속은 지금의 관계를 모형화하는 데 맞지 않습니다.

 

 

해결방법

set객체는 list 객체를 써서 구현되는 (is implemented in terms of) 형태의 설계가 가능하다는 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 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 데이터의 내부 표현부
};
template<typename T>
bool set<T>::member(const T& item) const
{
    return set<T>::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::size_t set<T>::size() const
{
    return rep.size();
}
cs

인라인 함수로 만들어도 될 정도의 구현입니다.

이 관계는 is – a가 아니라, is – implemented – in – terms – of입니다.

 

결론

객체 합성(composition)의 의미는 public 상속이 가진 의미와 완전히 다릅니다.
응용 영역에서 객체 합성의 의미는 has – a(… 는 …를 가짐)입니다.
구현 영역에서는 is – implemented – in – terms – of(… 는 …. 를 써서 구현됨)의 의미를 갖습니다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함
250x250