组合模式,是(构造型)结构型的设计模式之一。通过递归手段来构造树形的对象结构,并可以通过一个对象来访问整个对象树。
角色及职责
Component (树形结构的节点抽象)
为所有的对象定义统一的接口(公共属性,行为等的定义)
提供管理子节点对象的接口
[可选]提供管理父节点对象的接口
Leaf (树形结构的叶节点)
Component的实现子类–普通文件
Composite(树形结构的枝节点)
Component的实现子类–子目录
适用于:
单个对象和组合对象的使用具有一致性。将对象组合成树形结构以表示“部分–整体”
案例
#include <iostream>
#include <list>
using namespace std;
/*
*树形结构--共同接口--整体和部分
* 文件夹--普通文件--文件夹(三个中任意两个或三个类型的组合)
* 文件夹下又有文件夹和普通文件(典型的树形结构)
*/
/*抽象类--子树或者叶子节点均继承自他*/
class IFile
{
public:
virtual void display()=0;
virtual int add(IFile *file) = 0;
virtual int remove(IFile* file) = 0;
virtual list<IFile *> *getChild()=0;
string getName()
{
return m_name;
}
public :
string m_name;//子树(子文件夹)或者叶子节点(普通文件)的名字
int cnt;//记录当前对象的下一级对象(目录或文件)前面应该有的分隔符("--")数量
//--表示第几层子目录
//分隔符("--")数量表示当前目录相对根目录来说是第几层子目录
};
/*叶子节点--普通文件*/
class File : public IFile
{
public:
File(string name)//构造函数
{
m_name = name;
cnt = 0;
}
virtual void display()//普通文件的显示函数--直接显示名字
{
cout<<m_name<<endl;
}
/*不具备插入孩子属性--返回-1*/
virtual int add(IFile *file)
{
return -1;
}
/*不具备删除孩子属性--返回-1*/
virtual int remove(IFile* file)
{
return -1;
}
/*不具备获取孩子属性--返回-1*/
virtual list<IFile *>*getChild()
{
return NULL;
}
};
/*子树部分--目录
将目录和普通文件组合--组合模式
构成文件系统的树形图*/
class Dir : public IFile
{
private:
list<IFile*> * m_list;
public:
Dir(string name)
{
m_name = name;
m_list = new list<IFile *>;//为孩子起始位置分配空间--建立一个链表
m_list->clear();//链表清空
cnt = 0;//表示当前目录就是根目录--根目录的0级子目录
}
/*对于目录的显示函数--需要递归显示子目录*/
virtual void display()
{
/*1.获取当前目录孩子的起始位置--m_list*/
list<IFile*> *chil_list = getChild();
/*2.显示当前目录的名字*/
cout<<m_name<<endl;
/*3.如果孩子非空表示该目录有子目录或者文件
*如果为空表示是空文件夹,什么也不做--退出显示函数不进行递归
* --递归结束的条件--孩子为空(空目录或者普通文件都会结束递归)
*/
if(chil_list != NULL && !chil_list->empty())
{
this->cnt++;//3.1 当前目录有子目录或者文件--前面的分隔符数量加1
/*3.2 遍历当前目录的孩子--当前目录的文件或者子目录*/
for( list<IFile*>::iterator it = chil_list->begin();it != chil_list->end();it++)
{
for(int i = 0;i < this->cnt;i++)//3.3 根据cnt打印分隔符--层级越深,cnt越大--
//每一级目录的cnt表示他的下一级文件或者子目录相对于根目录(调用display函数的对象)的层级关系
cout<<"--";
(*it)->cnt = this->cnt;//3.4 将当前目录的cnt传承给下一级的文件或者子目录--
//--下一级文件或者目录前面的分隔符是在上一级的基础上递增的
(*it)->display();//3.5 递归显示每一个孩子节点
}
}
/*4. 在显示结束以后要将根目录(调用display函数的对象)的层级关系清零
--否则再次调用的时候会在上一次的基础上累加分隔符数量*/
cnt = 0;
}
/*向目录添加孩子--子目录或者文件*/
virtual int add(IFile *file)
{
m_list->push_back(file);//将孩子插入链表
return 0;
}
/*从目录删除孩子--从链表移除一个孩子节点*/
virtual int remove(IFile* file)
{
m_list->remove(file);
return 0;
}
/*获取孩子的起始位置*/
virtual list<IFile *>*getChild()
{
return m_list;
}
};
/*测试案例*/
int main()
{
Dir * root = new Dir("C");//根目录
root->display();
Dir * dir1 = new Dir("folder1");//子目录1
File * file1 = new File("a.txt");//文件1
root->add(dir1);
root->add(file1);
root->display();
Dir * dir2 = new Dir("folder2");//子目录2
File * file2 = new File("b.txt");//文件2
dir1->add(dir2);//子目录2添加到子目录1
dir2->add(file2);//文件2添加到子目录2
dir1->add(file2);//文件2添加到子目录1
root->add(file2);//文件2添加到根目录
root->display();//显示根目录的文件系统结构
return 0;
}