C++设计模式——组合模式(composite pattern)

一、原理讲解

1.1意图

将对象组合成树形结构以表示“部分 — 整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

1.2应用场景

  • 你想表示对象的部分 — 整体层次结构;
  • 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象;

1.3结构图(UML图)

组合模式UML结构图

1.4代码实现步骤

a1 定义组件类Compoment,定义四个接口函数add()、remove()、findChild()、operation();
a2 定义一个叶节点类Leaf,定义重写操作函数operation();
a3 定义一个子节点枝类重写基类四个接口函数add()、remove()、findChild()、operation();

二、实现代码

CompositePattern.cpp

#include <iostream>
#include <string>
#include <vector>

using namespace std;

#define DELETE(pointer) delete (pointer); (pointer)=nullptr

class Compoment
{
public:
	virtual ~Compoment() { cout << "~Compoment()" << endl; }
	virtual void operation() = 0;
	virtual void add(Compoment *com){}
	virtual void remove(Compoment *com){}
	virtual Compoment* findChild(int index) { return nullptr; }
};

class Leaf : public Compoment
{
	int num;
public:
	Leaf(int num) :num(num) {}
	~Leaf(){ cout << "~Leaf()" << num << endl; }
	virtual void operation() override { cout << "Leaf::operation()" << num << endl; }
};

class Composite : public Compoment
{
	vector<Compoment*> coms;
public:
	Composite() :coms(vector<Compoment*>()) {}
	~Composite() { // 如果后面被继承,则需要声明为虚析构函数
		cout << "~Composite()" << endl;
		for (auto it = coms.begin(); it != coms.end(); ++it)
			if ((*it) != nullptr) {				
				DELETE((*it));				
			}

	} 
	void operation() { 
		cout << "Composite::operation()" << endl;
		for (auto it : coms) {
			it->operation();
		}
	}
	void add(Compoment *com) { coms.push_back(com); }
	void remove(Compoment *com) {
		auto it = std::find(coms.begin(), coms.end(), com);
		if ((*it) != nullptr)
			coms.erase(it);
	}
	Compoment* findChild(int index) { 
		if (index < 0 || index >= coms.size()) {
			return nullptr;
		}
		return coms[index];
	}
};

void doCompositePattern()
{
	cout << " ------------- com1->operation() ------------- " << endl;
	Compoment *com1 = new Composite();
	com1->add(new Leaf(1));
	com1->add(new Leaf(2));
	com1->add(new Leaf(3));
	com1->operation();

	cout << "\n ------------- com2->operation() ------------- " << endl;
	Compoment *com2 = new Composite();
	com2->add(new Leaf(1));
	com2->add(com1); // 注意:这里已经把指针*com1加入了com2的vector指针列表,所以不能在外部手动释放com1内存,此时com1所指向的内存有两个指针(外面的*com1和vector里面的*com1)
	com2->operation();

	cout << "\n ------------- com2->findChild() ------------- " << endl;
	auto com3 = com2->findChild(0);
	if (com3 != nullptr)
	{
		cout << "\n ------------- com3->operation() ------------- " << endl;
		com3->operation();
	}
		
	cout << "\n ------------- delete com2 ------------- " << endl;
	DELETE(com2); // 只需要释放指针*com2就行,里面数组vector维护了指针*com1地址,com2对象析构时会自动释放数组所有堆内存
}

mian.cpp

#include <iostream>

extern void doCompositePattern();

int main()
{
	doCompositePattern();
	
	system("pause");
	return 1;
}

输出结果图

三、总结

组合模式的核心在于对象组合的树形结构,即“整体—部分”的思想,跟装饰者模式结构类似,但是侧重点不同。装饰者模式主要在于不添加子类的前提下给对象增加职责,而组合模式在于给对象组合成一个“整体—部分”的树形结构来调用。
注意:在使用时,要特别注意内存的释放,本文没有使用智能指针主要是为了熟悉内存管理,在项目中可以考虑上智能指针防止出现野指针和内存非法访问!

四、参考内容

C++设计模式-Composite组合模式
C++组合模式
陈建忠设计模式(参考:哔哩哔哩C++设计模式!!!)
Erich Gamma,Richard Helm.《设计模式 可复用面向对象软件的基础》[M].机械工业出版社,2019:


版权声明:本文为naibozhuan3744原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。