设计模式之组合模式

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_
#include <string>
class 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
Component

2.普通文件类 (Leaf)

#ifndef LEAF_H_
#define LEAF_H_
// This is a ordinary File class
#include "Component.h"
#include <iostream>
class 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)" <<std::endl;
}
Leaf

3.目录类(Composite)

#ifndef COMPOSITE_H_
#define COMPOSITE_H_

#include "Component.h"
#include <vector>
#include <iostream>
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<Component*> 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;
    }
    }
}
View Code

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);
}
View Code

安全组合模式

  透明组合模式中的不安全,其实就是说叶子结点中多了它不该有的操作——对分支结点进行管理的方法,例如add、remove、getChild。要想使它安全,那么最简单的办法就是把这些方法从公共基类里剔除,延迟到Composite类里去声明和实现,这样分支结点和叶子结点就不会有不安全的因素了。

1.抽象出普通文件和目录文件的基类(UML图中的Component)

#ifndef COMPONENT_H_
#define COMPONENT_H_
#include <string>
class Component
{
public:
    virtual void displayOwnInfo(const int depth) const = 0;
    Component() = default;
    virtual ~Component() = default;
protected:
    std::string m_strName;
};
#endif
Component

2.普通文件类(UML类图中的Leaf)

#ifndef LEAF_H_
#define LEAF_H_
// This is a ordinary File class
#include "Component.h"
#include <iostream>
class 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)" <<std::endl;
}
Leaf

3.目录文件类(UML类图中的Composite)

#ifndef COMPOSITE_H_
#define COMPOSITE_H_

#include "Component.h"
#include <vector>
#include <iostream>
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<Component*> 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;
    }
    }
}
Composite

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);
}
Client
原文地址:https://www.cnblogs.com/ToBeExpert/p/9682277.html