前言:鉴于此模式比较简单,在网上找到一篇例子还算不错,(原作者帖子),在此对原作者表示感谢。本博客在原博客的基础上增加了注释,修改了原作者代码中编译错误的地方,修改之后的代码已经在visual studio2017和linux上编译成功,并且运行正确。
先来说一下定义。组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。(来自大话设计模式)
此设计模式应用了一个很重要的概念。list,本代码使用为 list<Company* > mList;
list可以简单的理解为一个容器,里面存放着一系列数据,结构为现行双向链表结构,里面的数据是有若干个节点构成。
list特点:
(1)不使用连续的内存空间,由指针将有序的元素链接起来。
(2)可以在内部任何位置进行插入和删除操作。
(3)不能对内部元素进行随机的访问,即不支持数组表示法和随机访问。
本代码 list<Company* > mList;主要使用了push_back和remove操作,即向链表中添加元素和删除元素。
下面贴上自己画的UML图
何时使用组合模式?
需求中是体现部分与整体层次的结构时候,用户可以忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象时,
下面给出源码:main.cpp
#include <iostream>
#include <string>
#include "test.h"
using namespace std;
int main()
{
char p1[] = "木鸟飞公司";//见注1
ConcreteCompany com1(p1);//定义一个对象com1的实例,
char p2[]="总公司财务部";
FinanceDepartment com2(p2);//定义一个com2实例,
char p3[] = "总公司人力部";
HrDepartment com3(p3);//定义一个com3实例,
com1.attach(&com2);//见注2
com1.attach(&com3);
char p4[]="木鸟飞东北分公司";
ConcreteCompany com11(p4);//
char p5[] = "东北分公司财务部";
FinanceDepartment com22(p5);
char p6[] = "东北分公司人力部";
HrDepartment com33(p6);
com11.attach(&com22);//见注3
com11.attach(&com33);
com1.attach(&com11);
string str("-");
com1.display(str);//见注4
cout << endl;
com1.LineOfDuty(str);
return 0;
}
test.cpp
#include <iostream>
#include "test.h"
using namespace std;
void ConcreteCompany::attach(Company* cpn)
{
if (nullptr != cpn)
{
mList.push_back(cpn);
}
}
void ConcreteCompany::detach(Company* cpn)
{
if (nullptr != cpn)
{
mList.remove(cpn);
}
}
void ConcreteCompany::display(string str)
{
list<Company* >::iterator beg = mList.begin();
cout << str << mName << endl;
str = str + str;
for (; beg != mList.end(); beg++)
{
(*beg)->display(str);
}
}
void ConcreteCompany::LineOfDuty(string company)
{
list<Company* >::iterator beg = mList.begin();
string name = mName;
for (; beg != mList.end(); beg++)
{
(*beg)->LineOfDuty(name);
}
}
void HrDepartment::display(string str)
{
cout << str << mName << endl;
}
void HrDepartment::LineOfDuty(string company)
{
cout << company << "员工招聘培训管理!" << endl;
}
void FinanceDepartment::display(string str)
{
cout << str << mName << endl;
}
void FinanceDepartment::LineOfDuty(string company)
{
cout << company << "公司财务收支管理!" << endl;
}
test.h
#ifndef COMPOSITE_H
#define COMPOSITE_H
#include <list>
#include <string>
#include <iostream>
using namespace std;
class Company
{
public:
Company(char* name):mName(name){}
virtual void attach(Company* cpn){}
virtual void detach(Company* cpn){}
virtual void display(string str){}
virtual void LineOfDuty(string company){}
protected:
char* mName;
list<Company* > mList;
};
class ConcreteCompany:public Company{
public:
ConcreteCompany(char* name):Company(name){}
virtual void attach(Company* cpn);
virtual void detach(Company* cpn);
virtual void display(string str);
virtual void LineOfDuty(string company);
};
class HrDepartment:public Company{
public:
HrDepartment(char* name):Company(name){}
virtual void display(string str);
virtual void LineOfDuty(string company);
};
class FinanceDepartment:public Company{
public:
FinanceDepartment(char* name):Company(name){}
virtual void display(string str);
virtual void LineOfDuty(string company);
};
#endif
几点解释,大佬请绕行。
注1:这里定义一个字符串,没有使用const char * S=“abcde”;因为形参参数是char * 类型,和const char *不匹配,具体可参考我的上篇博客。
注2:把com2放进链表中,注意,com1并不在链表中,com1可以看成一个大树的树干,com2就是树干上的树枝。很好理解,因为com2是由com1 调用attach函数送入到链表中。
注3:同理com22和com33由com11调用attach函数送入链表中,此链表在com11分支上面,由此实现了一个树状结构,com1下面有com2、com3和com11,com11下面有com22和com33。
注4:com1.display(str);因为com1是ConcreteCompany类型,所以调用了void ConcreteCompany::display(string str)函数,在此函数中又对链表进行了遍历,由注3可知,这样会遍历出com2、com3、com11。当遍历到com2时候,由于com2是FinanceDepartment类型,所以(*beg)->display(str);函数会调用void FinanceDepartment::display(string str)函数。com3同理。com11由于又是ConcreteCompany类型,所以会出现函数自调用的情况,也就是会重复调用void ConcreteCompany::display(string str)。这也算就是本设计模式的精髓之所在。
运行结果: