composition(復(fù)合)是在 objects of one type(一個(gè)類(lèi)型的對(duì)象)包含 objects of another type(另一個(gè)類(lèi)型的對(duì)象)時(shí),types(類(lèi)型)之間的關(guān)系。例如:
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(對(duì)象)由 string,Address,和 PhoneNumber objects(對(duì)象)組成。在程序員中,術(shù)語(yǔ) composition(復(fù)合)有很多同義詞。它也可以稱(chēng)為 layering,containment,aggregation,和 embedding。
《C++箴言:確保公開(kāi)繼承模擬“is-a”》解釋了 public inheritance(公有繼承)意味著 "is-a"。composition(復(fù)合)也有一個(gè)含意。實(shí)際上,他有兩個(gè)含意。composition(復(fù)合)既意味著 "has-a"(有一個(gè)),又意味著 "is-implemented-in-terms-of"(是根據(jù)……實(shí)現(xiàn)的)。這是因?yàn)槟阋谀愕能浖刑幚韮蓚€(gè)不同的 domains(領(lǐng)域)。你程序中的一些 objects(對(duì)象)對(duì)應(yīng)你所模擬的世界里的東西,例如,people(人),vehicles(交通工具),video frames(視頻畫(huà)面)等等。這樣的 objects(對(duì)象)是 application domain(應(yīng)用領(lǐng)域)的部分。另外的 objects(對(duì)象)純粹是 implementation artifacts(實(shí)現(xiàn)的產(chǎn)物),例如,buffers(緩沖區(qū)),mutexes(互斥體),search trees(搜索樹(shù))等等。這些各類(lèi) objects(對(duì)象)定義應(yīng)你的軟件的 implementation domain(實(shí)現(xiàn)領(lǐng)域)。當(dāng) composition(復(fù)合)發(fā)生在 application domain(應(yīng)用領(lǐng)域)的 objects(對(duì)象)之間,它表達(dá)一個(gè) has-a(有一個(gè))的關(guān)系,當(dāng)它發(fā)生在 implementation domain(實(shí)現(xiàn)領(lǐng)域),它表達(dá)一個(gè) is-implemented-in-terms-of(是根據(jù)……實(shí)現(xiàn)的)的關(guān)系
上面的 Person class(類(lèi))示范了 has-a(有一個(gè))的關(guān)系。一個(gè) Person object(對(duì)象)has a(有一個(gè))名字,一個(gè)地址,以及語(yǔ)音和傳真電話(huà)號(hào)碼。你不能說(shuō)一個(gè)人 is a(是一個(gè))名字或一個(gè)人 is an(是一個(gè))地址。你可以說(shuō)一個(gè)人 has a(有一個(gè))名字和 has an(有一個(gè))地址。大多數(shù)人對(duì)此區(qū)別不難理解,所以混淆 is-a和 has-a(有一個(gè))之間的角色的情況非常少見(jiàn)。
is-a和 is-implemented-in-terms-of(是根據(jù)……實(shí)現(xiàn)的)之間的區(qū)別稍微有些棘手。例如,假設(shè)你需要一個(gè)類(lèi)的模板來(lái)表現(xiàn)相當(dāng)小的 objects(對(duì)象)的 sets,也就是說(shuō),排除重復(fù)的集合。因?yàn)?reuse(復(fù)用)是一件受人歡迎的事情,你的第一個(gè)直覺(jué)就是使用標(biāo)準(zhǔn)庫(kù)中的 set template(模板)。當(dāng)你能使用已經(jīng)被寫(xiě)好的東西時(shí),為什么還要寫(xiě)一個(gè)新的 template(模板)呢?
不幸的是,set 的典型實(shí)現(xiàn)導(dǎo)致每個(gè)元素三個(gè)指針的開(kāi)銷(xiāo)。這是因?yàn)?sets 通常被作為 balanced search trees(平衡搜索樹(shù))來(lái)實(shí)現(xiàn),這允許它們保證 logarithmic-time(對(duì)數(shù)時(shí)間)的 lookups(查找),insertions(插入)和 erasures(刪除)。當(dāng)速度比空間更重要的時(shí)候,這是一個(gè)合理的設(shè)計(jì),但是當(dāng)空間比速度更重要時(shí),對(duì)你的程序來(lái)說(shuō)就有問(wèn)題了。因而,對(duì)你來(lái)說(shuō),標(biāo)準(zhǔn)庫(kù)的 set 為你提供了不合理的交易。看起來(lái)你終究還是要寫(xiě)你自己的 template(模板)。
reuse(復(fù)用)依然是一件受人歡迎的事情。作為 data structure(數(shù)據(jù)結(jié)構(gòu))的專(zhuān)家,你知道實(shí)現(xiàn) sets 的諸多選擇,其中一種是使用 linked lists(線(xiàn)性鏈表)。你也知道標(biāo)準(zhǔn)的 C++ 庫(kù)中有一個(gè) list template(模板),所以你決定(復(fù))用它。
具體地說(shuō),你決定讓你的新的 Set template(模板)從 list 繼承。也就是說(shuō),Set 將從 list 繼承。畢竟,在你的實(shí)現(xiàn)中,一個(gè) Set object(對(duì)象)實(shí)際上就是一個(gè) list object(對(duì)象)。于是,你就像這樣聲明你的 Set template(模板):
template // the wrong way to use list for Set
class Set: public std::list { ... };
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(對(duì)象)由 string,Address,和 PhoneNumber objects(對(duì)象)組成。在程序員中,術(shù)語(yǔ) composition(復(fù)合)有很多同義詞。它也可以稱(chēng)為 layering,containment,aggregation,和 embedding。
《C++箴言:確保公開(kāi)繼承模擬“is-a”》解釋了 public inheritance(公有繼承)意味著 "is-a"。composition(復(fù)合)也有一個(gè)含意。實(shí)際上,他有兩個(gè)含意。composition(復(fù)合)既意味著 "has-a"(有一個(gè)),又意味著 "is-implemented-in-terms-of"(是根據(jù)……實(shí)現(xiàn)的)。這是因?yàn)槟阋谀愕能浖刑幚韮蓚€(gè)不同的 domains(領(lǐng)域)。你程序中的一些 objects(對(duì)象)對(duì)應(yīng)你所模擬的世界里的東西,例如,people(人),vehicles(交通工具),video frames(視頻畫(huà)面)等等。這樣的 objects(對(duì)象)是 application domain(應(yīng)用領(lǐng)域)的部分。另外的 objects(對(duì)象)純粹是 implementation artifacts(實(shí)現(xiàn)的產(chǎn)物),例如,buffers(緩沖區(qū)),mutexes(互斥體),search trees(搜索樹(shù))等等。這些各類(lèi) objects(對(duì)象)定義應(yīng)你的軟件的 implementation domain(實(shí)現(xiàn)領(lǐng)域)。當(dāng) composition(復(fù)合)發(fā)生在 application domain(應(yīng)用領(lǐng)域)的 objects(對(duì)象)之間,它表達(dá)一個(gè) has-a(有一個(gè))的關(guān)系,當(dāng)它發(fā)生在 implementation domain(實(shí)現(xiàn)領(lǐng)域),它表達(dá)一個(gè) is-implemented-in-terms-of(是根據(jù)……實(shí)現(xiàn)的)的關(guān)系
上面的 Person class(類(lèi))示范了 has-a(有一個(gè))的關(guān)系。一個(gè) Person object(對(duì)象)has a(有一個(gè))名字,一個(gè)地址,以及語(yǔ)音和傳真電話(huà)號(hào)碼。你不能說(shuō)一個(gè)人 is a(是一個(gè))名字或一個(gè)人 is an(是一個(gè))地址。你可以說(shuō)一個(gè)人 has a(有一個(gè))名字和 has an(有一個(gè))地址。大多數(shù)人對(duì)此區(qū)別不難理解,所以混淆 is-a和 has-a(有一個(gè))之間的角色的情況非常少見(jiàn)。
is-a和 is-implemented-in-terms-of(是根據(jù)……實(shí)現(xiàn)的)之間的區(qū)別稍微有些棘手。例如,假設(shè)你需要一個(gè)類(lèi)的模板來(lái)表現(xiàn)相當(dāng)小的 objects(對(duì)象)的 sets,也就是說(shuō),排除重復(fù)的集合。因?yàn)?reuse(復(fù)用)是一件受人歡迎的事情,你的第一個(gè)直覺(jué)就是使用標(biāo)準(zhǔn)庫(kù)中的 set template(模板)。當(dāng)你能使用已經(jīng)被寫(xiě)好的東西時(shí),為什么還要寫(xiě)一個(gè)新的 template(模板)呢?
不幸的是,set 的典型實(shí)現(xiàn)導(dǎo)致每個(gè)元素三個(gè)指針的開(kāi)銷(xiāo)。這是因?yàn)?sets 通常被作為 balanced search trees(平衡搜索樹(shù))來(lái)實(shí)現(xiàn),這允許它們保證 logarithmic-time(對(duì)數(shù)時(shí)間)的 lookups(查找),insertions(插入)和 erasures(刪除)。當(dāng)速度比空間更重要的時(shí)候,這是一個(gè)合理的設(shè)計(jì),但是當(dāng)空間比速度更重要時(shí),對(duì)你的程序來(lái)說(shuō)就有問(wèn)題了。因而,對(duì)你來(lái)說(shuō),標(biāo)準(zhǔn)庫(kù)的 set 為你提供了不合理的交易。看起來(lái)你終究還是要寫(xiě)你自己的 template(模板)。
reuse(復(fù)用)依然是一件受人歡迎的事情。作為 data structure(數(shù)據(jù)結(jié)構(gòu))的專(zhuān)家,你知道實(shí)現(xiàn) sets 的諸多選擇,其中一種是使用 linked lists(線(xiàn)性鏈表)。你也知道標(biāo)準(zhǔn)的 C++ 庫(kù)中有一個(gè) list template(模板),所以你決定(復(fù))用它。
具體地說(shuō),你決定讓你的新的 Set template(模板)從 list 繼承。也就是說(shuō),Set 將從 list 繼承。畢竟,在你的實(shí)現(xiàn)中,一個(gè) Set object(對(duì)象)實(shí)際上就是一個(gè) list object(對(duì)象)。于是,你就像這樣聲明你的 Set template(模板):
template // the wrong way to use list for Set
class Set: public std::list { ... };

