工程源码:
c++设计模式-结构型模式-组合模式https://download.csdn.net/download/qq_40788199/85665241码云:
设计模式-结构型模式-组合模式https://gitee.com/gongguixing/c-design-mode.git
1、组合模式的定义与特点
组合(Composite Pattern)模式的定义:有时又叫作整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型设计模式。
组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,树形结构图如下。
由上图可以看出,其实根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于用一种类型。但是在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义),让它们具备一致行为。
这样,在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的便利。
组合模式的主要优点有:
- 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
- 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
其主要缺点是:
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
- 不容易限制容器中的构件;
- 不容易用继承的方法来增加构件的新功能;
2、组合模式的结构与实现
1. 模式的结构
组合模式包含以下主要角色。
- 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
- 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
- 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
组合模式分为透明式的组合模式和安全式的组合模式。
3、分类
3.1、透明方式
在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。其结构图如图 1 所示。
3.2、安全方式
在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。其结构图如图 2 所示。
4、代码实现
假如要访问集合 c0={leaf1,{leaf2,leaf3}} 中的元素,其对应的树状图如图 3 所示。
编辑4.2透明组合模式
抽象构件(Component)角色
#ifndef ICOMPONENT_H
#define ICOMPONENT_H
#include <string>
using namespace std;
// 透明方式
// 抽象构件角色
// 抽象构件(Component)角色:
// 它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。
class IComponent
{
public:
virtual ~IComponent() {}
// 添加子对象
virtual void add(IComponent *c) = 0;
// 删除子对象
virtual void remove(IComponent *c) = 0;
// 获取指定子对象
virtual IComponent* getChild(uint8_t i) = 0;
// 方法调用
virtual void operation() = 0;
// 释放所有子对象
virtual void deleteChild() = 0;
// 获取唯一标识属性
virtual string &getKey() = 0;
// 获取节点类型
virtual string &getType() = 0;
};
#endif // ICOMPONENT_H
树叶构件(Leaf)角色
#ifndef LEAF_H
#define LEAF_H
#include "icomponent.h"
// 透明方式
// 树叶构件角色
// 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
class Leaf : public IComponent
{
public:
Leaf(const string &key);
void add(IComponent *c) override;
void remove(IComponent *c) override;
IComponent* getChild(uint8_t i)override;
void deleteChild() override;
void operation() override;
string &getKey() override;
string &getType() override;
private:
string mKey;
string mType;
};
#endif // LEAF_H
#include "leaf.h"
#include <iostream>
Leaf::Leaf(const string &key)
: mKey(key)
{
mType = "Leaf";
}
void Leaf::add(IComponent *c)
{
}
void Leaf::remove(IComponent *c)
{
}
IComponent *Leaf::getChild(uint8_t i)
{
return NULL;
}
void Leaf::deleteChild()
{
}
void Leaf::operation()
{
std::cout << "Leaf: " << mKey << " operation." << std::endl;
}
string &Leaf::getKey()
{
return mKey;
}
string &Leaf::getType()
{
return mType;
}
树枝构件(Composite)角色 / 中间构件
#ifndef COMPOSITE_H
#define COMPOSITE_H
#include "icomponent.h"
#include <vector>
// 透明方式
// 树枝构件角色 / 中间构件
// 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。
// 它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
class Composite : public IComponent
{
public:
Composite(const string &key);
void add(IComponent *c) override;
void remove(IComponent *c) override;
IComponent* getChild(uint8_t i)override;
void deleteChild() override;
void operation() override;
string &getKey() override;
string &getType() override;
private:
// 子对象容器
vector<IComponent*> mChildren;
// 唯一标识属性
string mKey;
// 节点类型
string mType;
};
#endif // COMPOSITE_H
#include "composite.h"
#include <iostream>
Composite::Composite(const string &key)
: mKey(key)
{
mType = "Composite";
}
void Composite::add(IComponent *c)
{
mChildren.push_back(c);
std::cout << "Composite: " << mKey << " add ";
std::cout << c->getType() << ": " << c->getKey() << std::endl;
}
void Composite::remove(IComponent *c)
{
uint8_t i = 0;
for (IComponent *p : mChildren)
{
// 地址一致则删除
if (p == c)
{
mChildren.erase(mChildren.begin() + i);
std::cout << "Composite: " << mKey << " remove ";
std::cout << c->getType() << ": " << c->getKey() << std::endl;
break;
}
i++;
}
}
IComponent *Composite::getChild(uint8_t i)
{
if (i <= mChildren.size())
{
return mChildren.at(i);
}
return NULL;
}
void Composite::deleteChild()
{
for (IComponent *p : mChildren)
{
std::cout << "Composite: " << mKey << " delete ";
std::cout << p->getType() << ": " << p->getKey() << std::endl;
// 树枝构件则释放子构件
if (p->getType().compare("Composite") == 0)
{
p->deleteChild();
delete p;
}
// 叶子构件直接释放
else if (p->getType().compare("Leef") == 0)
{
delete p;
}
}
mChildren.clear();
}
void Composite::operation()
{
for (IComponent *p : mChildren)
{
if (p)
{
p->operation();
}
}
}
string &Composite::getKey()
{
return mKey;
}
string &Composite::getType()
{
return mType;
}
调用示例
#include <iostream>
#include "leaf.h"
#include "composite.h"
#include "sleaf.h"
#include "scomposite.h"
int main()
{
// 透明方式调用
// 创建树枝构件
IComponent *c0 = new Composite("c0");
IComponent *c1 = new Composite("c1");
// 创建叶子构件
IComponent *leaf1 = new Leaf("leaf1");
IComponent *leaf2 = new Leaf("leaf2");
IComponent *leaf3 = new Leaf("leaf3");
// c0添加叶子leaf1
c0->add(leaf1);
// c0添加树枝c1
c0->add(c1);
// c1添加叶子leaf2
c1->add(leaf2);
// c1添加叶子leaf3
c1->add(leaf3);
// c0方法调用
c0->operation();
// 释放所有子节点
c0->deleteChild();
return 0;
}
4.2、安全组合模式
安全式的组合模式与透明式组合模式的实现代码类似,只要对其做简单修改就可以了,代码如下。
抽象构件(Component)角色
#ifndef ISCOMPONENT_H
#define ISCOMPONENT_H
#include <string>
using namespace std;
// 透明方式
// 抽象构件角色
// 抽象构件(Component)角色:
// 它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。
// 在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成
class ISComponent
{
public:
virtual ~ISComponent() {}
// 方法调用
virtual void operation() = 0;
// 获取唯一标识属性
virtual string &getKey() = 0;
// 获取节点类型
virtual string &getType() = 0;
};
树叶构件(Leaf)角色:
#ifndef SLEAF_H
#define SLEAF_H
#include "iscomponent.h"
// 安全方式
// 树叶构件角色
// 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
class SLeaf : public ISComponent
{
public:
SLeaf(const string &key);
void operation() override;
string &getKey() override;
string &getType() override;
private:
string mKey;
string mType;
};
#endif // SLEAF_H
#include "sleaf.h"
#include <iostream>
SLeaf::SLeaf(const string &key)
: mKey(key)
{
mType = "SLeaf";
}
void SLeaf::operation()
{
std::cout << "SLeaf: " << mKey << " operation." << std::endl;
}
string &SLeaf::getKey()
{
return mKey;
}
string &SLeaf::getType()
{
return mType;
}
树枝构件(Composite)角色 / 中间构件
#ifndef SCOMPOSITE_H
#define SCOMPOSITE_H
#include "iscomponent.h"
#include <vector>
// 安全方式
// 树枝构件角色 / 中间构件
// 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。
// 它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
class SComposite : public ISComponent
{
public:
SComposite(const string &key);
void add(ISComponent * p);
void remove(ISComponent *c) ;
ISComponent* getChild(uint8_t i);
void deleteChild();
void operation() override;
string &getKey() override;
string &getType() override;
private:
vector<ISComponent*> mChildren;
string mKey;
string mType;
};
#endif // SCOMPOSITE_H
#include "scomposite.h"
#include <iostream>
SComposite::SComposite(const string &key)
: mKey(key)
{
mType = "SComposite";
}
void SComposite::add(ISComponent *p)
{
mChildren.push_back(p);
std::cout << "SComposite: " << mKey << " add ";
std::cout << p->getType() << ": " << p->getKey() << std::endl;
}
void SComposite::remove(ISComponent *c)
{
uint8_t i = 0;
for (ISComponent *p : mChildren)
{
if (p == c)
{
mChildren.erase(mChildren.begin() + i);
std::cout << "Composite: " << mKey << " remove ";
std::cout << c->getType() << ": " << c->getKey() << std::endl;
break;
}
i++;
}
}
ISComponent *SComposite::getChild(uint8_t i)
{
if (i <= mChildren.size())
{
return mChildren.at(i);
}
return NULL;
}
void SComposite::deleteChild()
{
for (ISComponent *p : mChildren)
{
std::cout << "SComposite: " << mKey << " delete ";
std::cout << p->getType() << ": " << p->getKey() << std::endl;
if (p->getType().compare("SComposite") == 0)
{
SComposite * s = (SComposite*)p;
if (s)
{
s->deleteChild();
}
delete p;
}
else if (p->getType().compare("SLeef") == 0)
{
delete p;
}
}
mChildren.clear();
}
void SComposite::operation()
{
for (ISComponent *p : mChildren)
{
if (p)
{
p->operation();
}
}
}
string &SComposite::getKey()
{
return mKey;
}
string &SComposite::getType()
{
return mType;
}
调用示例
#include <iostream>
#include "leaf.h"
#include "composite.h"
#include "sleaf.h"
#include "scomposite.h"
int main()
{
// 安全方式调用
// 创建树枝构件
SComposite *sc0 = new SComposite("sc0");
SComposite *sc1 = new SComposite("sc1");
// 创建叶子构件
ISComponent *sleaf1 = new SLeaf("sleaf1");
ISComponent *sleaf2 = new SLeaf("sleaf2");
ISComponent *sleaf3 = new SLeaf("sleaf3");
// sc0添加叶子sleaf1
sc0->add(sleaf1);
// sc0添加树枝sc1
sc0->add(sc1);
// sc1添加叶子sleaf2
sc1->add(sleaf2);
// sc1添加叶子sleaf3
sc1->add(sleaf3);
// sc0方法调用
sc0->operation();
// 释放所有子节点
sc0->deleteChild();
return 0;
}
组合模式的应用场景
前面分析了组合模式的结构与特点,下面分析它适用的以下应用场景。
- 在需要表示一个对象整体与部分的层次结构的场合。
- 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。