5.组合模式

目录

一、概念

  • 组合模式又叫整体--部分模式
  • 组合模式描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无须对它们进行区分
  • 可以一致地对待容器对象和叶子对象。
  • 它使树型结构的问题中,模糊了简单元素复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

二、类图详解

  • 包含一个抽象组件类Component、叶子结点类Leaf和组件集合类Composite
  • 抽象组件类:组合中的对象声明接口,实现所有类共有接口的行为。声明用于访问和管理Component的子部件的接口。
  • 叶子结点:叶节点对象,就是终端独立结点,不能addRemove其它组件。
  • 组件集合类:实现Component的相关操作,可以继续包含组件集合类和叶子结点。
  • CompositeComponent类形成迭代关联关系。

三、组合模式实现步骤

  • 定义抽象组件接口
  • 实现叶子节点类,实现抽象组件类的接口。
  • 实现组件集合类,实现抽象组件类的接口。
  • 定义环境类,将叶子节点和组件集合加入根组件集合。

四、代码实现

  • 生成目录树结构,目录里面可以再放目录或文件
  • 最终的输出如下

4.1 Component类

  • 顶层接口
cpp 复制代码
#pragma once
#include <iostream>

class Component
{
public:
	std::string name;  //叶子结点和组件结点都有名字和类型
	bool m_isLeaf;     //是否是叶子结点(暂时没用)

	Component(const std::string& name, bool isLeaf)
	{
		this->name = name;
		this->m_isLeaf = isLeaf;
	}

	virtual void Add(Component* c) = 0;
	virtual void Remove(Component* c) = 0;
	virtual void Display(int depth) = 0;

	bool isLeaf() const
	{
		return this->m_isLeaf;
	}
};

4.2 Composite类

  • 用来放文件夹或文件
cpp 复制代码
#pragma once
#include <iostream>
#include <list>

#include "Component.h"

class Composite: public Component
{
private:
	std::list<Component*> m_component;

public:
	Composite(const std::string& name) : Component(name, false)
	{

	}

	void Add(Component* c) override
	{
		m_component.push_back(c);
	}

	void Remove(Component* c) override
	{
		m_component.remove(c);
	}

	void Display(int depth) override
	{
		std::cout << std::string(depth, '-') << name << std::endl;
		for (auto item : m_component)
		{
			item->Display(depth + 2);
		}
	}
};

4.3 Leaf类

  • 用来放文件
cpp 复制代码
#pragma once
#include <iostream>
#include "Component.h"

class Leaf: public Component
{
public:
	Leaf(const std::string& name): Component(name, true)
	{

	}

	void Add(Component* c) override
	{
		std::cout << "Can't add component to leaf." << std::endl;
	}

	void Remove(Component* c) override
	{
		std::cout << "Can't remove component to leaf." << std::endl;
	}

	void Display(int depth) override
	{
		std::cout << std::string(depth, '-') << name << std::endl;
	}
};

4.4 测试

cpp 复制代码
#include "Leaf.h"
#include "Composite.h"

class Client
{
private:
	Component* com;

public:
	Client(Component* obj)
	{
		com = obj;
	}

	void display(int n)
	{
		com->Display(n);
	}
};

int main()
{
	Component* root = new Composite("根目录");
	root->Add(new Leaf("根目录下的文件A"));
	root->Add(new Leaf("根目录下的文件B"));

	Component* dir1 = new Composite("根目录下的文件夹FA");
	dir1->Add(new Leaf("文件夹FA下的文件A"));
	dir1->Add(new Leaf("文件夹FA下的文件B"));

	root->Add(dir1);

	Component* dir2 = new Composite("根目录FA下的文件夹FAX");
	dir2->Add(new Leaf("文件夹FAX下的文件A"));
	dir2->Add(new Leaf("文件夹FAX下的文件B"));

	dir1->Add(dir2);

	root->Add(new Leaf("根目录下的文件C"));
	root->Add(new Leaf("根目录下的文件D"));

	Client c(root);
	c.display(1);
	
	return 0;
}

五、扩展

  • 设计一个饭店的菜单
  • 可以再包含一个菜单或者菜单项
  • 类图如下
  • 菜单项图结构如下
  • 代码部分请参阅 https://gitee.com/piglittle/design_patterns中的 Head_First_Design_Partterns解决方案下的 composite_pattern项目