2018-09-20 16:12:07
组合模式(Composite)
组合模式(Composite),将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象(即叶子构件)和组合对象(即组合构件)的使用具有一致性。(例如,你可以在word里对单个字和一行字采用同样的操作)注意,这里说的树就是一颗树,没有任何的限制,它可以是任何形状的。这棵树它是靠对象之间的组合关系构建起来,而非数据结构意义上的树。
何时使用组合模式
当你发现需求中是体现部分整体层次的结构(树状结构)时,以及你希望用户可以忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象时,就应该考虑组合模式了。
组合模式UML类图
组合模式根据实现的不同可以分为两种:透明方式组合模式、安全方式组合模式
透明方式组合模式UML类图:
在透明组合模式中,Component是应用了组合模式的所有构件的基类,它提供了整个模式结构中所有子类的公共方法,另外提供了用于管理以及访问子构件的方法(例如Add、Remove、GetChild)
Leaf在这个结构中表示叶子结点,就像数据结构中的树一样,叶子结点不能有子节点。所以很明显的问题是,叶子结点实现用于管理子构件的方法:Add、Remove、GetChild毫无意义。这个就是透明组合模式存在的问题。
Composite:在它的内部有一个包含Component指针对象的列表,因为在整个模式中,Composite表示的是一个分支结点,分支结点下又可能有数量不定的分支结点,这个列表的作用就是保存分支结点的子节点用的。在Composite中实现了用于管理子节点的方法:Add、Remove、GetChild。
透明组合模式的好处在于,对client来说屏蔽了叶子节点和分支结点的差异。但client必须要明确的知道哪个是叶子结点,否则在使用上可能会出现一些问题。
安全组合模式UML类图
从上图可以看出安全组合模式和透明组合模式最大的差别就是Component基类不再具有所有子类的方法,这样Leaf就不必实现一些不必要的方法了,例如对分支结点的管理方法。
组合模式的优缺点
优点:
1.组合模式可以清楚的定义分层次的复杂对象,表示对象的全部或部分层次。
2.当用户希望忽略组合对象和单个对象的不同时,采用组合模式,提高了软件的可复用性。因此此时,client代码可以一致的使用组合结构或者其中的单个对象。
3.在组合结构中,添加新的节点或者叶子是一件很容易的事情。因为每个子类都提供了对它所拥有的构建进行管理的能力,所以此时无需对原有的类进行修改。
4.为树形结构提供了一种灵活的解决方案,通过递归组合叶子和分支结点可以组合出结构复杂的树,但同时client代码对树的管理的复杂度不会提高。
缺点:
增大了设计难度。在进行设计时你需要先抽象出整个树的结构,区分清楚哪些应该是叶子,但问题在于不是所有的结点都必须会和叶子有关联。
代码示例
模仿文件系统:文件系统中有两类文件,一种是目录(文件夹)、另外一种就是普通文件,目录下可以包含目录也可以包含普通文件,当时普通文件下是不能包含任何类型的文件的。整个文件系统构成了一个树形结构。既然普通文件和目录都是文件,那么我们可以抽象出一个文件基类。另外,由普通文件下不能再包含任何类型文件可知,普通文件是目录树一个分支上的节点。接下来我们来模拟一下这个树形的文件系统。
透明组合模式
1.抽象出一个文件和文件夹的基类:
#ifndef COMPONENT_H_#define COMPONENT_H_#includeclass Component{public: virtual void add(Component *value) = 0; virtual void remove(Component *value) = 0; virtual Component* getChild(const int iIndex) = 0; virtual void displayOwnInfo(const int depth) const = 0; Component() = default; virtual ~Component() = default;protected: std::string m_strName;};#endif
2.普通文件类 (Leaf)
#ifndef LEAF_H_#define LEAF_H_// This is a ordinary File class#include "Component.h"#includeclass Leaf : public Component{public: void add(Component* value) override; void remove(Component *value) override; Component* getChild(const int iIndex) override; void displayOwnInfo(const int depth) const override; Leaf(const std::string strName) { m_strName = strName; } ~Leaf() = default;};#endif#include "Leaf.h"void Leaf::add(Component* value){ std::cout << "I am just a Leaf,There is nothing to do!" << std::endl;}void Leaf::remove(Component* value){ std::cout << "I am just a Leaf.This is nothing to do!" << std::endl;}Component* Leaf::getChild(const int iIndex){ std::cout << "I am just a Leaf.There is nothing to do!" << std::endl; return nullptr;}void Leaf::displayOwnInfo(const int depth) const{ std::string strOut('+',depth); std::cout << strOut << m_strName<<".I am a Leaf(Ordinary File)" <
3.目录类(Composite)
#ifndef COMPOSITE_H_#define COMPOSITE_H_#include "Component.h"#include#include class Composite:public Component{public: void add(Component *value); void remove(Component *value); Component* getChild(const int iIndex); void displayOwnInfo(const int depth) const; Composite(const std::string strName) { m_strName = strName; } ~Composite(); Composite() = delete;private: std::vector m_vecComponent;};#endif#include "Composite.h"void Composite::add(Component *value){ m_vecComponent.push_back(value);}void Composite::remove(Component *value){ auto iter = m_vecComponent.begin(); while(iter != m_vecComponent.end()) { if(*iter == value) { if(nullptr != *iter) { delete value; value = nullptr; m_vecComponent.erase(iter); break; } } ++iter; }}Component* Composite::getChild(const int iIndex){ if(iIndex <0 || iIndex >= m_vecComponent.size()) return nullptr; return m_vecComponent[iIndex];}void Composite::displayOwnInfo(const int depth) const{ std::string strOut('+',depth); std::cout << strOut << m_strName << "I am a Dir!" << std::endl; for(auto it : m_vecComponent) { it->displayOwnInfo(depth+2); }}Composite::~Composite(){ for(auto it : m_vecComponent) { if(it != nullptr) { delete it; it = nullptr; } }}
4.客户端(client)
#include "Composite.h"#include "Leaf.h"using namespace std;int main(int argc,char *argv[]){ //Create a Tree //Create a Root Node Composite *pRoot = new Composite("Root"); //Create a Leaf Leaf *pIniFile = new Leaf("Ini.txt"); pRoot->add(pIniFile); //Remove Leaf pRoot->remove(pIniFile); //Create a Branch Composite *p2Level = new Composite("2Level"); Leaf *p2LevelLeaf1 = new Leaf("2LevelLeaf1"); p2Level->add(p2LevelLeaf1); Composite *p3Level = new Composite("3Level"); Leaf *p3LevelLeaf1 = new Leaf("3LevelLeaf1"); p3Level->add(p3LevelLeaf1); p2Level->add(p3Level); pRoot->add(p2Level); pRoot->displayOwnInfo(1); return (1);}
安全组合模式
透明组合模式中的不安全,其实就是说叶子结点中多了它不该有的操作——对分支结点进行管理的方法,例如add、remove、getChild。要想使它安全,那么最简单的办法就是把这些方法从公共基类里剔除,延迟到Composite类里去声明和实现,这样分支结点和叶子结点就不会有不安全的因素了。
1.抽象出普通文件和目录文件的基类(UML图中的Component)
#ifndef COMPONENT_H_#define COMPONENT_H_#includeclass Component{public: virtual void displayOwnInfo(const int depth) const = 0; Component() = default; virtual ~Component() = default;protected: std::string m_strName;};#endif
2.普通文件类(UML类图中的Leaf)
#ifndef LEAF_H_#define LEAF_H_// This is a ordinary File class#include "Component.h"#includeclass Leaf : public Component{public: void displayOwnInfo(const int depth) const override; Leaf(const std::string strName) { m_strName = strName; } ~Leaf() = default;};#endif#include "Leaf.h"void Leaf::displayOwnInfo(const int depth) const{ std::string strOut(depth,'+'); std::cout << strOut << m_strName<<".I am a Leaf(Ordinary File)" <
3.目录文件类(UML类图中的Composite)
#ifndef COMPOSITE_H_#define COMPOSITE_H_#include "Component.h"#include#include class Composite:public Component{public: void add(Component *value); void remove(Component *value); Component* getChild(const int iIndex); void displayOwnInfo(const int depth) const; Composite(const std::string strName) { m_strName = strName; } ~Composite(); Composite() = delete;private: std::vector m_vecComponent;};#endif#include "Composite.h"void Composite::add(Component *value){ m_vecComponent.push_back(value);}void Composite::remove(Component *value){ auto iter = m_vecComponent.begin(); while(iter != m_vecComponent.end()) { if(*iter == value) { if(nullptr != *iter) { delete value; value = nullptr; m_vecComponent.erase(iter); break; } } ++iter; }}Component* Composite::getChild(const int iIndex){ if(iIndex <0 || iIndex >= m_vecComponent.size()) return nullptr; return m_vecComponent[iIndex];}void Composite::displayOwnInfo(const int depth) const{ std::string strOut(depth,'+'); std::cout << strOut << m_strName << "I am a Dir!" << std::endl; for(auto it : m_vecComponent) { it->displayOwnInfo(depth+2); }}Composite::~Composite(){ for(auto it : m_vecComponent) { if(it != nullptr) { delete it; it = nullptr; } }}
4.main函数(UML类图中的Client)
#include "Composite.h"#include "Leaf.h"using namespace std;int main(int argc,char *argv[]){ //Create a Tree //Create a Root Node Composite *pRoot = new Composite("Root"); //Create a Leaf Leaf *pIniFile = new Leaf("Ini.txt"); pRoot->add(pIniFile); //Remove Leaf pRoot->remove(pIniFile); //Create a Branch Composite *p2Level = new Composite("2Level"); Leaf *p2LevelLeaf1 = new Leaf("2LevelLeaf1"); p2Level->add(p2LevelLeaf1); Composite *p3Level = new Composite("3Level"); Leaf *p3LevelLeaf1 = new Leaf("3LevelLeaf1"); p3Level->add(p3LevelLeaf1); p2Level->add(p3Level); pRoot->add(p2Level); pRoot->displayOwnInfo(1); return (1);}