[翻譯] Effective C++, 3rd Edition, Item 38: 通過 composition(複合)類比 “has-a”(有一個)或 “is-i

來源:互聯網
上載者:User

Item 38: 通過 composition(複合)類比 "has-a"(有一個)或 "is-implemented-in-terms-of"(是根據……實現的)

作者:Scott Meyers

譯者:fatalerror99 (iTePub's Nirvana)

發布:http://blog.csdn.net/fatalerror99/

composition(複合)是在 objects of one type(一個類型的對象)包含 objects of another type(另一個類型的對象)時,types(類型)之間的關係。例如:

class Address { ... };             // where someone lives

class PhoneNumber { ... };

class Person {
public:
  ...

private:
  std::string name;               // composed object
  Address address;                // ditto
  PhoneNumber voiceNumber;        // ditto
  PhoneNumber faxNumber;          // ditto
};

此例之中,Person objects(對象)由 string,Address,和 PhoneNumber objects(對象)組成。在程式員中,術語 composition(複合)有很多同義字。它也可以稱為 layeringcontainmentaggregation,和 embedding

Item 32 解釋了 public inheritance(公有繼承)意味著 "is-a"(是一個)。composition(複合)也有一個含意。實際上,他有兩個含意。composition(複合)既意味著 "has-a"(有一個),又意味著 "is-implemented-in-terms-of"(是根據……實現的)。這是因為你要在你的軟體中處理兩個不同的 domains(領域)。你程式中的一些 objects(對象)對應你所類比的世界裡的東西,例如,people(人),vehicles(交通工具),video frames(視頻畫面)等等。這樣的 objects(對象)是 application domain(應用領域)的部分。另外的 objects(對象)純粹是 implementation artifacts(實現的產物),例如,buffers(緩衝區),mutexes(互斥體),search trees(搜尋樹)等等。這些各類 objects(對象)定義應你的軟體的 implementation domain(實現領域)。當 composition(複合)發生在 application domain(應用領域)的 objects(對象)之間,它表達一個 has-a(有一個)的關係,當它發生在 implementation domain(實現領域),它表達一個 is-implemented-in-terms-of(是根據……實現的)的關係

上面的 Person class(類)示範了 has-a(有一個)的關係。一個 Person object(對象)has a(有一個)名字,一個地址,以及語音和傳真呼叫號碼。你不能說一個人 is a(是一個)名字或一個人 is an(是一個)地址。你可以說一個人 has a(有一個)名字和 has an(有一個)地址。大多數人對此區別不難理解,所以混淆 is-a(是一個)和 has-a(有一個)之間的角色的情況非常少見。

is-a(是一個)和 is-implemented-in-terms-of(是根據……實現的)之間的區別稍微有些棘手。例如,假設你需要一個類的模板來表現相當小的 objects(對象)的 sets,也就是說,排除重複的集合。因為 reuse(複用)是一件受人歡迎的事情,你的第一個直覺就是使用標準庫中的 set template(模板)。當你能使用已經被寫好的東西時,為什麼還要寫一個新的 template(模板)呢?

不幸的是,set 的典型實現導致每個元素三個指標的開銷。這是因為 sets 通常被作為 balanced search trees(平衡搜尋樹)來實現,這允許它們保證 logarithmic-time(對數時間)的 lookups(尋找),insertions(插入)和 erasures(刪除)。當速度比空間更重要的時候,這是一個合理的設計,但是當空間比速度更重要時,對你的程式來說就有問題了。因而,對你來說,標準庫的 set 為你提供了不合理的交易。看起來你終究還是要寫你自己的 template(模板)。

reuse(複用)依然是一件受人歡迎的事情。作為 data structure(資料結構)的專家,你知道實現 sets 的諸多選擇,其中一種是使用 linked lists(線性鏈表)。你也知道標準的 C++ 庫中有一個 list template(模板),所以你決定(複)用它。

具體地說,你決定讓你的新的 Set template(模板)從 list 繼承。也就是說,Set<T> 將從 list<T> 繼承。畢竟,在你的實現中,一個 Set object(對象)實際上就是一個 list object(對象)。於是,你就像這樣聲明你的 Set template(模板):

template<typename T>                       // the wrong way to use list for Set
class Set: public std::list<T> { ... };

在這裡,看起來每件事情都很好。但實際上有一個很大的錯誤。就像 Item 32 中的解釋,如果 D is-a(是一個)B,對於 B 成立的每一件事情對 D 也成立。然而,一個 list object(對象)可以包含重複,所以如果值 3051 被插入一個 list<int> 兩次,那個 list 將包含 3051 的兩個拷貝。與此對照,一個 Set 不可以包含重複,所以如果值 3051 被插入一個 Set<int> 兩次,那個 set 只包含該值的一個拷貝。因此一個 Set is-a(是一個)list 是不正確的,因為對 list objects(對象)成立的某些事情對 Set objects(對象)不成立。

因為這兩個 classes(類)之間的關係不是 is-a(是一個),public inheritance(公有繼承)不是類比這個關係的正確方法。正確的方法是認識到一個 Set object(對象)可以 be implemented in terms of a list object(是根據一個 list 對象實現的):

template<class T>                   // the right way to use list for Set
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;                 // representation for Set data
};

Set 的 member functions(成員函數)可以極大程度地依賴 list 和標準庫的其它部分已經提供的機能,所以只要你熟悉了用 STL 編程的基本方法,實現就非常簡單了:

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 =               // see Item 42 for info on
    std::find(rep.begin(), rep.end(), item);         // "typename" here
  if (it != rep.end()) rep.erase(it);
}

template<typename T>
std::size_t Set<T>::size() const
{
  return rep.size();
}

這些函數足夠簡單,使它們成為 inlining(內聯化)的合理候選者,可是我知道在堅定 inlining(內聯化)的決心之前,你可能需要回顧一下 Item 30 中的討論。

一個令人信服的觀點是,根據 Item 18 的關於將 interfaces(介面)設計得易於正確使用,而難以錯誤使用的論述,如果要遵循 STL container(容器)的慣例,Set 的 interface(介面)應該更多,但是在這裡遵循那些慣例就需要在 Set 中填充大量 stuff(材料),這將使得它和 list 之間的關係變得曖昧不清。因為這個關係是本 Item 的重點,我們用教學的清晰性替換了 STL 的相容性。除此之外,Set 的 interface(介面)的幼稚不應該遮掩關於 Set 的無可爭辯的正確:它和 list 之間的關係。這個關係不是 is-a(是一個)(雖然最初看上去可能很像),而是 is-implemented-in-terms-of(是根據……實現的)。

Things to Remember

  • composition(複合)與 public inheritance(公有繼承)的意義完全不同。
  • 在 application domain(應用領域)中,composition(複合)意味著 has-a(有一個)。在 implementation domain(實現領域)中意味著 is-implemented-in-terms-of(是根據……實現的)。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.