目錄
- 前言
- 問題
- 解決方案
- 結構
- 代碼
前言
組合是一種結構型設計模式,你可以使用它將對象組合成樹狀結構,并且能像使用獨立對象一樣使用它們。
問題
如果應用的核心模型能用樹狀結構表示, 在應用中使用組合模式才有價值。
例如, 你有兩類對象:產品 和 盒子。 一個盒子中可以包含多個產品或者幾個較小的盒子 。 這些小盒子 中同樣可以包含一些產品 或更小的 盒子 , 以此類推。
假設你希望在這些類的基礎上開發一個定購系統。 訂單中可以包含無包裝的簡單產品, 也可以包含裝滿產品的盒子……以及其他盒子。 此時你會如何計算每張訂單的總價格呢?
你可以嘗試直接計算: 打開所有盒子, 找到每件產品, 然后計算總價。 這在真實世界中或許可行, 但在程序中, 你并不能簡單地使用循環語句來完成該工作。 你必須事先知道所有 產品 和盒子的類別, 所有盒子的嵌套層數以及其他繁雜的細節信息。 因此, 直接計算極不方便, 甚至完全不可行。
解決方案
組合模式建議使用一個通用接口來與 產品 和盒子進行交互, 并且在該接口中聲明一個計算總價的方法。
那么方法該如何設計呢? 對于一個產品, 該方法直接返回其價格; 對于一個盒子, 該方法遍歷盒子中的所有項目, 詢問每個項目的價格, 然后返回該盒子的總價格。 如果其中某個項目是小一號的盒子, 那么當前盒子也會遍歷其中的所有項目, 以此類推, 直到計算出所有內部組成部分的價格。 你甚至可以在盒子的最終價格中增加額外費用, 作為該盒子的包裝費用。
該方式的最大優點在于你無需了解構成樹狀結構的對象的具體類。 你也無需了解對象是簡單的產品還是復雜的盒子。 你只需調用通用接口以相同的方式對其進行處理即可。 當你調用該方法后, 對象會將請求沿著樹結構傳遞下去。
結構
代碼
#include <iostream>
#include <memory>
#include <list>
#include <cstdlib> // 包含 rand() 和 srand()
#include <ctime> // 包含 time() 用于獲取系統時間作為種子
using namespace std;class Component{
public:virtual int execute()=0;virtual ~Component(){}
};
class Leaf:public Component{
public:int execute() override{int r=rand()%10;cout<<"返回葉子節點隨機數: "<<r<<endl;return r;}
};
class Composite:public Component{
public:int execute() override{int sum=0;for(auto child:m_children){sum+=child->execute();}return sum;}void add(shared_ptr<Component> c){m_children.push_back(c);}void remove(shared_ptr<Component> c){for(auto it=m_children.begin();it!=m_children.end();it++){if(*it==c){it = m_children.erase(it); // erase返回下一個有效迭代器break; // 找到并刪除后可以退出循環}}}list<shared_ptr<Component>> getChildren(){return m_children;}
private:list<shared_ptr<Component>> m_children;
};int main(){srand(time(nullptr));auto root=make_shared<Composite>();auto leaf01=make_shared<Leaf>();auto leaf02=make_shared<Leaf>();auto node01=make_shared<Composite>();auto leaf03=make_shared<Leaf>();auto leaf04=make_shared<Leaf>();root->add(leaf01);root->add(leaf02);node01->add(leaf03);node01->add(leaf04);root->add(node01);int sum=root->execute();cout<<"----------------"<<endl;cout<<"總和為: "<<sum<<endl;root->remove(leaf02);sum=root->execute();cout<<"----------------"<<endl;cout<<"刪除一葉子后,總和為: "<<sum<<endl;return 0;
}