39.通过复合塑造出"has-a"或"根据某物实现出"

前言

 
复合(composition)是类型之间的一种关系,其意义为:某种类型的对象内含其它类型的对象。举例而言,Address、Name是独立的class,而Person class将它们作为成员变量,这便是一种复合。


复合

 
众所周知,public继承意味着is-a关系。复合则意味着has-a(有一个)或者is-implemented-in-terms-of(根据某物实现出)。其意义具体是has-a,还是根据某物实现出,取决于你当前软件系统中处理的领域(domain)。

应用域与实现域

在软件设计中,对象有的是真实世界的某些事物在计算机逻辑层面的映射,比如人物,汽车,住址等等,这些对象属于应用域。
有些对象是实现细节上的人工制品,比如缓冲区,互斥器,查找树等等。这些对象属于实现域。
当复合发生于应用域内时,类型之间表现出has-a的关系。当它发生于实现域内时,类型之间则表现出“根据某物实现出”的关系。

关系判定

很少有人会把把has-a与is-a搞混,比如说人有一个地址,而绝非人是一个地址。关系判定的难点在于区分is-a与is-implemented-in-terms-of.

问题实例

假设当前我们希望由list派生出一个set(不基于平衡树实现的理由是此应用场景空间性能比时间性能更重要)我们可能会写出这样的东西:

1
2
template <typename T>
class set:public list<T> {}

这种设计显然是错误的,public继承意味着is-a,也就是每一个set必然也是一个list,但我们都知道list可以存放多个相同元素,set则不行,它们之间绝非is-a关系,因为对list对象成立的某些东西在set对象中并不成立。

问题解决

正确的做法是根据list实现出set,也就是判定它们之间的关系为“根据某物实现出”:

1
2
3
4
5
6
7
8
9
10
template<typename T>
class set{
public:
bool member(const T& item) const;
void insert(const T& item);
void remove(const T& item);
size_t size() const;
private:
list<T> rep;//复合
}


总结

  1. 复合与public继承的意义完全不同
  2. 在应用域,复合意味着has-a。在实现域,符合意味着根据某物实现出。