接口隔离原则:面向对象设计之接口隔离原则-CSDN博客
设计模式工厂模式 : 设计模式之工厂模式-CSDN博客
迭代器模式:设计模式之迭代器模式-CSDN博客
适配器模式:设计模式之适配器模式-CSDN博客
过滤器模式:设计模式之过滤器模式-CSDN博客
空对象模式:设计模式之空对象模式-CSDN博客
桥接模式:设计模式之桥接模式-CSDN博客
责任链模式:设计模式之责任链模式-CSDN博客
策略模式:设计模式之策略模式-CSDN博客
Pimpl技法:C++之Pimpl惯用法-CSDN博客
单例模式:设计模式之单例模式-CSDN博客
组合模式:设计模式之组合模式-CSDN博客
观察者模式:设计模式之观察者模式-CSDN博客
目录
1.概述
组合模式(Composite Pattern),又叫部分整体模式,它将对象组合成树形结构以表示部分 - 整体的层次结构。组合模式是一种行为型设计模式,这种模式使得用户对单个对象和组合对象的使用具有一致性。组合模式主要应用于需要表示复杂对象结构或者需要将对象组合成树形结构的场景。 组合模式通过一种巧妙的设计方案,可以一致性地处理整个树形结构或者树形结构的一部分,也可以一致性地处理树形结构中的叶子节点(不包含子节点的节点)和容器节点。 组合模式主要解决了在对象组合树中,如何实现一致性的操作和处理的问题。它使得用户可以方便地对对象树进行操作,而不需要考虑对象的具体类型。组合模式还提供了一种灵活的方式来表示对象结构,可以方便地添加和删除对象。
2.结构
UML类图结构如下:
角色定义:
**组件(Component):**定义了组合中所有对象的通用接口,可以是抽象类或接口。它声明了用于访问和管理子组件的方法,包括添加、删除、获取子组件等。
**叶子节点(Leaf):**表示组合中的叶子节点对象,叶子节点没有子节点。它实现了组件接口的方法,但通常不包含子组件。
**复合节点(Composite):**表示组合中的复合对象,复合节点可以包含子节点,可以是叶子节点,也可以是其他复合节点。它实现了组件接口的方法,包括管理子组件的方法。
组件适配结点(ComponentAdapter): 这个是组件的适配器对象,主要是为它的子节点服务的,它的子节点就不用啰嗦的去实现Component的接口,这个节点在模式结构中不是必须的。
**客户端(Client):**通过组件接口与组合结构进行交互,客户端不需要区分叶子节点和复合节点,可以一致地对待整体和部分。
在Component中声明所有用来管理子对象的方法,其中包括add、remove等,这样实Component接口的所有子类都具备了add和remove。这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备 完全一致的行为接口。但问题也很明显,因为Leaf类本身不具备add()、remove()方法的功能,所以实现它是没有意义的。
3.实现
以公司管理为例,简单来说,公司分为普通员工和领导,那么根据组合模式的原来说来,普通员工就是叶子节点,领导要管理普通员工就是组合对象,详细的代码如下:
cpp
#include <string>
#include <assert.h>
#include <vector>
#include <algorithm>
#include <memory>
#include <QDebug>
//抽象职员对象
class IEmployee {
//构造函数
public:
explicit IEmployee(const std::string& name, const std::string& dept, int sal) {
this->m_name = name;
this->m_dept = dept;
this->m_salary = sal;
}
virtual ~IEmployee() {}
virtual void add(IEmployee* e) = 0;
virtual void remove(IEmployee* e) = 0;
virtual void doWork() = 0;
virtual std::string toString() const = 0;
protected:
std::string m_name;
std::string m_dept;
int m_salary;
};
//职员适配器对象
class EmployeeAdapter : public IEmployee {
public:
explicit EmployeeAdapter(const std::string& name, const std::string& dept, int sal)
: IEmployee(name, dept, sal) {}
void add(IEmployee* e) override {
assert(0);
}
void remove(IEmployee* e) override {
assert(0);
}
void doWork() override {
assert(0);
}
std::string toString() const override {
assert(0);
return "";
}
};
//普通职员
class CCommonEmployee : public EmployeeAdapter
{
public:
explicit CCommonEmployee(const std::string& name, const std::string& dept, int sal)
: EmployeeAdapter(name, dept, sal) {}
void doWork() override {
}
std::string toString() const override {
return (std::string("Employee :[ Name : ") + m_name
+ std::string(", dept : ") + m_dept + std::string(", salary :")
+ std::to_string(m_salary) + std::string(" ]\n"));
}
};
//公司领导
class CEmployeeLeader : public EmployeeAdapter
{
public:
explicit CEmployeeLeader(const std::string& name, const std::string& dept, int sal)
: EmployeeAdapter(name, dept, sal) {}
void doWork() override {
}
void add(IEmployee* e) override {
m_employees.emplace_back(e);
}
void remove(IEmployee* e) override {
m_employees.erase(std::remove_if(m_employees.begin(), m_employees.end(), [e](auto& it) {
return e == it; }));
}
std::string toString() const override {
std::string temp = (std::string("Employee :[ Name : ") + m_name
+ std::string(", dept : ") + m_dept + std::string(", salary :")
+ std::to_string(m_salary) + std::string(" ]\n"));
for (auto& it : m_employees) {
temp += it->toString();
}
return temp;
}
private:
std::vector<IEmployee*> m_employees;
};
这段代码定义了一个抽象基类IEmployee
,其中包含三个方法:add
、remove、doWork和toString
。CCommonEmployee
和CEmployeeLeader
类继承自IEmployee
并实现了这些方法。CEmployeeLeader
类还包含一个IEmployee
指针的向量,表示其子组件。这种结构允许您统一处理单个对象(CCommonEmployee
)和对象的组合(CEmployeeLeader
)。
测试例程:
cpp
int main()
{
std::unique_ptr<IEmployee> p1(new CCommonEmployee("zdxiao", "Software", 34));
std::unique_ptr<IEmployee> p2(new CCommonEmployee("lidonghua", "Software", 25));
std::unique_ptr<IEmployee> p3(new CCommonEmployee("huazi", "Software", 40));
std::unique_ptr<IEmployee> p4(new CCommonEmployee("zhoujie", "Software", 20));
std::unique_ptr<IEmployee> p7(new CCommonEmployee("zhouyun", "Hardware", 36));
std::unique_ptr<IEmployee> p8(new CCommonEmployee("luojiou", "Hardware", 22));
std::unique_ptr<IEmployee> p9(new CCommonEmployee("yuanmi", "Hardware", 33));
std::unique_ptr<IEmployee> p10(new CCommonEmployee("yuanjie", "Hardware", 41));
std::unique_ptr<IEmployee> softwareLeader(new CEmployeeLeader("liuyong", "Software Leader", 50));
std::unique_ptr<IEmployee> hardwareLeader(new CEmployeeLeader("wangyujie", "Hardware Leader", 43));
std::unique_ptr<IEmployee> ceo(new CEmployeeLeader("liubei", "CEO", 60));
softwareLeader->add(p1.get());
softwareLeader->add(p2.get());
softwareLeader->add(p3.get());
softwareLeader->add(p4.get());
hardwareLeader->add(p7.get());
hardwareLeader->add(p8.get());
hardwareLeader->add(p9.get());
hardwareLeader->add(p10.get());
ceo->add(softwareLeader.get());
ceo->add(hardwareLeader.get());
std::string des = ceo->toString();
qDebug() << des.data();
return 0;
}
输出:
cpp
Employee :[ Name : liubei, dept : CEO, salary :60 ]
Employee :[ Name : liuyong, dept : Software Leader, salary :50 ]
Employee :[ Name : zdxiao, dept : Software, salary :34 ]
Employee :[ Name : lidonghua, dept : Software, salary :25 ]
Employee :[ Name : huazi, dept : Software, salary :40 ]
Employee :[ Name : zhoujie, dept : Software, salary :20 ]
Employee :[ Name : wangyujie, dept : Hardware Leader, salary :43 ]
Employee :[ Name : zhouyun, dept : Hardware, salary :36 ]
Employee :[ Name : luojiou, dept : Hardware, salary :22 ]
Employee :[ Name : yuanmi, dept : Hardware, salary :33 ]
Employee :[ Name : yuanjie, dept : Hardware, salary :41 ]
4.使用场景
组合模式是一种行为型设计模式,适用于多种场景,如树形对象结构、统一对象处理、动态责任、可变责任和 UI 工具包等。在实际应用中,组合模式与其他设计模式密切相关,共同解决各种问题。以下是组合模式应对的一些场景:
1.树形对象结构 : 组合模式适用于实现树形对象结构,如组织结构、文件系统、UI 元素等。这种结构可以方便地表示部分 - 整体的层次关系。在组合模式中,对象通过递归组合形成树状结构,使得客户代码可以统一处理简单元素和复杂元素。
2.统一对象处理 : 组合模式允许客户代码以统一的方式处理简单元素和复杂元素。这种统一处理方式可以简化客户端代码,提高代码的可维护性和可扩展性。通过组合模式,客户端可以忽略对象的复杂内部结构,而仅关注对象的整体行为。
3.动态责任 : 组合模式允许在运行时动态地为单个对象添加责任,而无需修改现有代码。这种动态责任分配可以提高系统的灵活性,使其能够适应不断变化的需求。在组合模式中,可以通过将新的责任分配给现有的对象来实现动态责任。
4.可变责任 : 组合模式适用于责任可能随时间变化的场景。在组合模式中,对象可以具有不同的责任,这使得系统能够适应不断变化的需求。通过组合模式,可以轻松地为对象添加新的责任,而无需修改现有代码。
5.UI 工具包 : 组合模式在 UI 工具包中具有广泛的应用。在这种场景下,顶级 UI 元素由许多较小的独立底层 UI 元素组成,这些元素都响应相同的事件和操作。通过使用组合模式,可以轻松地管理 UI 元素的层次结构,并确保它们以一致的方式响应事件。
6.计费系统 : 在计费系统中,组合模式可以应用于处理复杂的活动记录。在这种场景下,重要的是能够生成正确的计费,而不关心活动的具体细节。通过使用组合模式,可以将复杂的活动记录组织成一个树形结构,以便生成正确的计费。 组合模式与其他设计模式密切相关,它们共同解决各种实际问题。在实际应用中,可以根据具体需求和场景,将组合模式与其他设计模式结合使用,以实现更加灵活、高效和可扩展的软件系统。
5.总结
优点
1.更好的扩展性:组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码。
2.更好的复用性:更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足"开闭原则"。
缺点
1.设计较复杂:客户端需要花更多时间理清类之间的层次关系。
2.不容易限制容器中的构件:不容易用继承的方法来增加构件的新功能。
3.叶子和树枝的声明都是实现类,违反了依赖倒置原则。
总的来说,使用组合模式可以使整体和部分用统一的组合组件去处理,使组合与单个透明化为统一的组合,并且组合中调用组合实现树状的递归调用,这样避免的判断分支,实现统一化处理。但如果单个对象的某些操作隐藏,成为默认操作,这样不安全,如果安全性侧重的话,可以让单个对象和组合对象继承不同的抽象类或实现不同的接口,用安全性同样增加了分支判断。用组合模式的透明性在递归的环节组合对象操作也包括单个对象操作,这样在单一职责的原则上也有损耗,真体的使用场景需要具体分析。