C++设计模式六、组合模式。

本文通过一个具体的案例详细解析了组合模式的概念与实现方法。利用C++代码展示如何构建一个‘部分-整体’的层次结构,使用户能够一致地处理单个对象与组合对象。

前言:鉴于此模式比较简单,在网上找到一篇例子还算不错,(原作者帖子),在此对原作者表示感谢。本博客在原博客的基础上增加了注释,修改了原作者代码中编译错误的地方,修改之后的代码已经在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)。这也算就是本设计模式的精髓之所在。

运行结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值