设计模式之组合(compose)模式
树形结构在软件中随处可见,比如操作系统中的目录结构,公司组织结构等等,如何运用面向对象的方式来处理这种树形结构是组合模式需要解决的问题。组合模式通过一种巧妙的设计方案来使得用户可以一致性地处理整个树形结构或者树形结构的一部分,也可以一致地处理树形结构中的叶子节点(不包含子节点的节点)和容器节点(包含子节点的节点).
一 组合模式简介
1.1 模式概述
组合(Composite)模式:组合多个对象形成树形结构以表示具有“整体-部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“部分-整体”(Part-Whole)模式,它是一种对象结构型模式。
1.2 需求介绍
M公司开发部想要开发一个杀毒软件,该软件既可以针对某个文件夹杀毒,也可以针对某个指定的文件进行杀毒。该杀毒软件还可以根据各类文件的特点,为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现需要提供该杀毒软件的整体框架设计方案。
二 杀毒软件框架设计
首先在D盘创建如下树结构:
2.1 类图
2.2 代码实现
2.2.1 抽象文件类
#pragma once #include <vector> #include <map> #include <iostream> using namespace std; // 抽象文件管理类 class CAbsFile { public: CAbsFile(){} virtual ~CAbsFile(){} virtual void AddFile(CAbsFile *pFile) { m_FileVect.push_back(pFile); } virtual void RemoveFile(string strFileName) { vector<CAbsFile*>::iterator iter; for (iter= m_FileVect.begin(); iter != m_FileVect.end(); iter ++) { if (0 == strFileName.compare((*iter)->m_strFileName)) { m_FileVect.erase(iter); } } } virtual CAbsFile* GetChild(string strFileName) { vector<CAbsFile*>::iterator iter; for (iter= m_FileVect.begin(); iter != m_FileVect.end(); iter ++) { if (0 == strFileName.compare((*iter)->m_strFileName)) { return *iter; } } return NULL; } virtual void PrintFileStruct() { cout << m_strFileName.c_str() <<endl; vector<CAbsFile*>::iterator iter; for (iter= m_FileVect.begin(); iter != m_FileVect.end(); iter ++) { CAbsFile *pFile = *iter; pFile->PrintFileStruct(); } } virtual void DeleteObject() { vector<CAbsFile*>::iterator iter; for (iter= m_FileVect.begin(); iter != m_FileVect.end(); iter ++) { CAbsFile *pFile = *iter; pFile->DeleteObject(); } delete this; } virtual void KillVirus() = 0; public: vector<CAbsFile*> m_FileVect; string m_strFileName; };
2.2.2 具体text文件类
class CTextFile : public CAbsFile { public: CTextFile(string strFileName) { m_strFileName = strFileName; cout << "CTextFile Construct" << endl; } ~CTextFile() { cout << "CTextFile Deconstruct" << endl; } void KillVirus() { cout << "对文本文件:" << m_strFileName.c_str() << "进行杀毒操作" << endl; } };
2.2.3 具体img文件类
class CImgFile : public CAbsFile { public: CImgFile(string strFileName) { m_strFileName = strFileName; cout << "CImgFile Construct" << endl; } ~CImgFile() { cout << "CImgFile Deconstruct" << endl; } void KillVirus() { cout << "对图像文件:" << m_strFileName.c_str() << "进行杀毒操作" << endl; } };
2.2.4 具体文件夹类
class CFolder : public CAbsFile { public: CFolder(string strFileName) { m_strFileName = strFileName; cout << "CFolder Construct" << endl; } ~CFolder() { cout << "CFolder Deconstruct" << endl; } void KillVirus() { cout << "对文件夹:" << m_strFileName.c_str() << "进行杀毒操作" << endl; vector<CAbsFile*>::iterator iter; for (iter= m_FileVect.begin(); iter != m_FileVect.end(); iter ++) { CAbsFile *pFile = *iter; pFile->KillVirus(); } } };
2.3 代码测试
#include "stdio.h" #include "compose.h" void main() { // 创建文件对象 cout << "创建文件对象" << endl; CAbsFile *pFolderD = new CFolder("本地磁盘D"); CAbsFile *pFolderComposeTest = new CFolder("ComposeTest"); CAbsFile *pFolderImgFiles = new CFolder("ImgFiles"); CAbsFile *pFolderTextFiles = new CFolder("TextFiles"); CAbsFile *pImgFile = new CImgFile("Image.png"); CAbsFile *pTextFile = new CTextFile("1.txt"); // 构建文件组合 pFolderImgFiles->AddFile(pImgFile); pFolderTextFiles->AddFile(pTextFile); pFolderComposeTest->AddFile(pFolderImgFiles); pFolderComposeTest->AddFile(pFolderTextFiles); pFolderD->AddFile(pFolderComposeTest); // 打印文件夹结构 cout << "打印本地磁盘D文件结构" << endl; pFolderD->PrintFileStruct(); cout <<endl; // 对composeTest文件夹进程杀毒操作 cout << "对composeTest文件夹进程杀毒操作" << endl; pFolderComposeTest->KillVirus(); cout << endl; // 对单个img文件进行杀毒操作 cout << "对单个img文件进行杀毒操作" << endl; pImgFile->KillVirus(); cout <<endl; cout << "释放文件对象" << endl; pFolderD->DeleteObject(); return; }
在具体实现时,可以创建图形界面让用户自己选择所需操作的根节点,无需修改源代码,符合开闭原则,客户端无须关心节点的层次结构,可以对所选节点进行统一处理,提高系统的灵活性。
三 组合模式小结
3.1 主要优点
(1)可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使客户忽略了层次的差异,方便对整个层次结构进行控制。
(2)增加新的容器构件和叶子构件都十分方便,无需对现有类库代码进行任何修改,符合开闭原则。
(3)为树形结构的面向对象实现提供了灵活地解决方案,可以形成复杂的树形结构,但对树形结构的控制却很简单。
3.2 主要缺点
增加新构件时很难对容器中的构建类型进行限制。
3.3 适用场景
(1)在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待他们。
(2)在一个使用面向对象语言开发的系统中需要处理一个树形结构。