我所理解的設計模式(C++實現)——組合模式(Composite Pattern)
解決的問題:
我們PC用到的檔案系統,其實就是我們資料結構裡的樹形結構,我們處理樹中的每個節點時,其實不用考慮他是葉子節點還是根節點,因為他們的成員函式都是一樣的,這個就是組合模式的精髓。他模糊了簡單元素和複雜元素的概念,客戶程式可以向處理簡單元素一樣來處理複雜元素,從而使得客戶程式與複雜元素的內部結構解耦。
將物件組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得使用者對單個物件和組合物件的使用具有一致性。
註明:樹形結構裡的葉子節點也有左右孩子,只不過他的孩子都是空。
概述
組合模式的實現根據所實現介面的區別分為兩種形式,分別稱為安全模式和透明模式。組合模式可以不提供父物件的管理方法,但組合模式必須在合適的地方提供子物件的管理方法(諸如:add、remove、getChild等)。
透明方式
作為第一種選擇,在Component裡面宣告所有的用來管理子類物件的方法,包括add()、remove(),以及getChild()方法。這樣做的好處是所有的構件類都有相同的介面。在客戶端看來,樹葉類物件與合成類物件的區別起碼在介面層次上消失了,客戶端可以同等同的對待所有的物件。這就是透明形式的組合模式。
這個選擇的缺點是不夠安全,因為樹葉類物件和合成類物件在本質上是有區別的。樹葉類物件不可能有下一個層次的物件,因此add()、remove()以及getChild()方法沒有意義,是在編譯時期不會出錯,而只會在執行時期才會出錯或者說識別出來。
安全方式
第二種選擇是在Composite類裡面宣告所有的用來管理子類物件的方法。這樣的做法是安全的做法,因為樹葉型別的物件根本就沒有管理子類物件的方法,因此,如果客戶端對樹葉類物件使用這些方法時,程式會在編譯時期出錯。
這個選擇的缺點是不夠透明,因為樹葉類和合成類將具有不同的介面。
這兩個形式各有優缺點,需要根據軟體的具體情況做出取捨決定。
類圖結構及樣例實現:
安全方式的組合模式:
這種形式涉及到三個角色:
抽象構件(Component)角色:這是一個抽象角色,它給參加組合的物件定義出公共的介面及其預設行為,可以用來管理所有的子物件。在安全式的合成模式裡,構件角色並不是定義出管理子物件的方法,這一定義由樹枝構件物件給出。
樹葉構件(Leaf)角色:樹葉物件是沒有下級子物件的物件,定義出參加組合的原始物件的行為。
樹枝構件(Composite)角色:代表參加組合的有下級子物件的物件。樹枝物件給出所有的管理子物件的方法,如add()、remove()、getChild()等。
樣例實現:
//Menu.h
#include <string>
class Menu
{
public:
virtual ~Menu();
virtual void Add(Menu*);
virtual void Remove(Menu*);
virtual Menu* GetChild(int);
virtual void Display() = 0;
protected:
Menu();
Menu(std::string);
std::string m_strName;
};
//Menu.cpp
#include "stdafx.h"
#include "Menu.h"
Menu::Menu()
{
}
Menu::Menu(std::string strName) : m_strName(strName)
{
}
Menu::~Menu()
{
}
void Menu::Add(Menu* pMenu)
{}
void Menu::Remove(Menu* pMenu)
{}
Menu* Menu::GetChild(int index)
{
return NULL;
}
//SubMenu.h
#include "Menu.h"
class SubMenu : public Menu
{
public:
SubMenu();
SubMenu(std::string);
virtual ~SubMenu();
void Display();
};
//SubMenu.cpp
#include "stdafx.h"
#include "SubMenu.h"
#include <iostream>
using namespace std;
SubMenu::SubMenu()
{
}
SubMenu::SubMenu(string strName) : Menu(strName)
{
}
SubMenu::~SubMenu()
{
}
void SubMenu::Display()
{
cout << m_strName << endl;
}
//CompositMenu.h
#include "Menu.h"
#include <vector>
class CompositMenu : public Menu
{
public:
CompositMenu();
CompositMenu(std::string);
virtual ~CompositMenu();
void Add(Menu*);
void Remove(Menu*);
Menu* GetChild(int);
void Display();
private:
std::vector<Menu*> m_vMenu;
};
//CompositMenu.cpp
#include "stdafx.h"
#include "CompositMenu.h"
#include <iostream>
using namespace std;
CompositMenu::CompositMenu()
{
}
CompositMenu::CompositMenu(string strName) : Menu(strName)
{
}
CompositMenu::~CompositMenu()
{
}
void CompositMenu::Add(Menu* pMenu)
{
m_vMenu.push_back(pMenu);
}
void CompositMenu::Remove(Menu* pMenu)
{
m_vMenu.erase(&pMenu);
}
Menu* CompositMenu::GetChild(int index)
{
return m_vMenu[index];
}
void CompositMenu::Display()
{
cout << "+" << m_strName << endl;
vector<Menu*>::iterator it = m_vMenu.begin();
for (; it != m_vMenu.end(); ++it)
{
cout << "|-";
(*it)->Display();
}
}
#include "stdafx.h"
#include "Menu.h"
#include "SubMenu.h"
#include "CompositMenu.h"
int main(int argc, char* argv[])
{
Menu* pMenu = new CompositMenu("國內新聞");
pMenu->Add(new SubMenu("時事新聞"));
pMenu->Add(new SubMenu("社會新聞"));
pMenu->Display();
pMenu = new CompositMenu("國際新聞");
pMenu->Add(new SubMenu("國際要聞"));
pMenu->Add(new SubMenu("環球視野"));
pMenu->Display();
return 0;
}
實現要點:
1.組合模式採用樹形結構來實現普遍存在的物件容器,從而將“一對多”的關係轉化“一對一”的關係,使得客戶程式碼可以一致地處理物件和物件容器,無需關心處理的是單個的物件,還是組合的物件容器。
2.將“客戶程式碼與複雜的物件容器結構”解耦是組合模式的核心思想,解耦之後,客戶程式碼將與純粹的抽象介面——而非物件容器的復內部實現結構——發生依賴關係,從而更能“應對變化”。
3.組合模式中,是將“Add和Remove等和物件容器相關的方法”定義在“表示抽象物件的Component類”中,還是將其定義在“表示物件容器的Composite類”中,是一個關乎“透明性”和“安全性”的兩難問題,需要仔細權衡。這裡有可能違背物件導向的“單一職責原則”,但是對於這種特殊結構,這又是必須付出的代價。
4.組合模式在具體實現中,可以讓父物件中的子物件反向追溯;如果父物件有頻繁的遍歷需求,可使用快取技巧來改善效率。
5. 客戶端儘量不要直接呼叫樹葉類的方法,而是藉助其父類(Component)的多型性完成呼叫,這樣可以增加程式碼的複用性。
使用場景:
以下情況下適用組合模式:
1.你想表示物件的部分-整體層次結構。
2.你希望使用者忽略組合物件與單個物件的不同,使用者將統一地使用組合結構中的所有物件。
參考資料:
相關文章
- C#設計模式-組合模式(Composite Pattern)C#設計模式
- composite pattern(組合模式)模式
- [C++設計模式] composite 組合模式C++設計模式
- 使用C# (.NET Core) 實現組合設計模式 (Composite Pattern)C#設計模式
- 設計模式系列之組合模式(Composite Pattern)——樹形結構的處理設計模式
- 設計模式之組合模式(Composite)分享設計模式
- 《設計模式》 - 7. 組合模式( Composite )設計模式
- 組合模式(Composite)模式
- 【C++設計模式】組合模式C++設計模式
- 【設計模式自習室】結構型:組合模式 Composite設計模式
- C++設計模式——命令模式(command pattern)C++設計模式
- 【設計模式】組合模式設計模式
- 設計模式-組合模式設計模式
- 設計模式《組合模式》設計模式
- C++設計模式——職責鏈模式( Chain of Responsibility Pattern)C++設計模式AI
- Android理解設計模式之組合模式、迭代器模式、訪問者模式Android設計模式
- js設計模式–組合模式JS設計模式
- js設計模式--組合模式JS設計模式
- 設計模式系列 – 組合模式設計模式
- 軟體設計模式————(組合模式)設計模式
- 徒手擼設計模式-組合模式設計模式
- PHP 設計模式之組合模式PHP設計模式
- GoLang設計模式20 - 組合模式Golang設計模式
- 極簡設計模式-組合模式設計模式
- 設計模式【11】-- 搞定組合模式設計模式
- 《Head First 設計模式》:組合模式設計模式
- 設計模式系列之「組合模式」設計模式
- 設計模式之策略模式和狀態模式(strategy pattern & state pattern)設計模式
- 設計模式-裝飾模式(Decorator Pattern)設計模式
- 設計模式之代理模式(proxy pattern)設計模式
- 設計模式--建造者模式(Builder Pattern)設計模式UI
- 設計模式--原型模式(Prototype Pattern)設計模式原型
- 設計模式--裝飾模式(Decorator Pattern)設計模式
- 設計模式-狀態模式(State Pattern)設計模式
- 設計模式實戰 - 抽象工廠模式(Abstract Factory Pattern)設計模式抽象
- 設計模式實戰 – 抽象工廠模式(Abstract Factory Pattern)設計模式抽象
- Java設計模式之(十)——組合模式Java設計模式
- 【趣味設計模式系列】之【組合模式】設計模式
- 23種設計模式之組合模式設計模式