组合模式:将对象组合成树形结构以表示'部分-整体'的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式的两种实现:
- 透明方式:在Component中声明所有用来管理子对象的方法,其中包括Add、Remove等。这样实现Component接口的所有子类都具备了Add和Remove。这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备完全一致的行为接口。但问题也很明显,因为Leaf类本身不具备Add()、Remove()方法的功能,所以实现它是没有意义的。
- 安全方式:在Component接口中不去声明Add和Remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,这样做就不会出现刚才提到的问题,不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。
需求

为一家在全国许多城市都有分销机构的大公司做办公管理系统,总部有人力资源、财务、运营等部门。
需求分析
- 希望总公司的组织结构,比如人力资源部、财务部的管理功能可以复用于分公司
代码
业务类
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Company{
char *name;
void (*add)(struct Company *, struct Company *);
void (*remove)(struct Company *, struct Company *);
void (*display)(struct Company *, int);
void (*lineOfDuty)(struct Company *);
} Company;
typedef struct ConcreteCompany {
Company base;
Company **children;
int childrenNum;
} ConcreteCompany;
void ConcreteCompanyAdd(Company *obj, Company *child) {
((ConcreteCompany *)obj)->children[((ConcreteCompany *)obj)->childrenNum++] = child;
}
void ConcreteCompanyRemove(Company *obj, Company *child) {
int n = ((ConcreteCompany *)obj)->childrenNum;
for(int i=0; i<n; i++) {
if(strcmp(((ConcreteCompany *)obj)->children[i]->name, child->name) == 0) {
free(((ConcreteCompany *)obj)->children[i]);
((ConcreteCompany *)obj)->children[i] = ((ConcreteCompany *)obj)->children[((ConcreteCompany *)obj)->childrenNum-1];
((ConcreteCompany *)obj)->childrenNum--;
break;
}
}
}
void ConcreteCompanyDisplay(Company *obj, int depth) {
for(int i=0;i<depth; i++) {
printf("-");
}
printf("%s\n", obj->name);
int n = ((ConcreteCompany *)obj)->childrenNum;
for(int i=0; i<n; i++) {
((ConcreteCompany *)obj)->children[i]->display(((ConcreteCompany *)obj)->children[i], depth+1);
}
}
void ConcreteCompanyLineOfDuty(Company *obj) {
int n = ((ConcreteCompany *)obj)->childrenNum;
for(int i=0; i<n; i++) {
((ConcreteCompany *)obj)->children[i]->lineOfDuty(((ConcreteCompany *)obj)->children[i]);
}
}
Company *InitConcreteCompany(char *name) {
ConcreteCompany *obj = (ConcreteCompany *)malloc(sizeof(ConcreteCompany));
obj->childrenNum = 0;
// 假设最多10个子公司和部门
obj->children = (Company **)malloc(sizeof(Company *)*10);
obj->base.name = name;
obj->base.add = ConcreteCompanyAdd;
obj->base.remove = ConcreteCompanyRemove;
obj->base.display = ConcreteCompanyDisplay;
obj->base.lineOfDuty = ConcreteCompanyLineOfDuty;
return (Company *)obj;
}
typedef struct HRDepartment {
Company base;
} HRDepartment;
void HRDepartmentDisplay(Company *obj, int depth) {
for(int i=0;i<depth; i++) {
printf("-");
}
printf("%s\n", obj->name);
}
void HRDepartmentLineOfDuty(Company *obj) {
printf("%s 员工招聘培训管理\n", obj->name);
}
Company *InitHRDepartment(char *name) {
HRDepartment *obj = (HRDepartment *)malloc(sizeof(HRDepartment));
obj->base.display = HRDepartmentDisplay;
obj->base.lineOfDuty = HRDepartmentLineOfDuty;
obj->base.name = name;
return (Company *)obj;
}
typedef struct FinanceDepartment {
Company base;
} FinanceDepartment;
void FinanceDepartmentDisplay(Company *obj, int depth) {
for(int i=0;i<depth; i++) {
printf("-");
}
printf("%s\n", obj->name);
}
void FinanceDepartmentLineOfDuty(Company *obj) {
printf("%s 公司财务收支管理\n", obj->name);
}
Company *InitFinanceDepartment(char *name) {
FinanceDepartment *obj = (FinanceDepartment *)malloc(sizeof(FinanceDepartment));
obj->base.display = FinanceDepartmentDisplay;
obj->base.lineOfDuty = FinanceDepartmentLineOfDuty;
obj->base.name = name;
return (Company *)obj;
}
客户端
隐藏子部门和分公司的区别
c
int main() {
Company *root = InitConcreteCompany("北京总公司");
root->add(root, InitHRDepartment("总公司人力资源部"));
root->add(root, InitFinanceDepartment("总公司财务部"));
Company *comp = InitConcreteCompany("上海华东分公司");
comp->add(comp, InitHRDepartment("华东分公司人力资源部"));
comp->add(comp, InitFinanceDepartment("华东分公司财务部"));
root->add(root, comp);
Company *comp1 = InitConcreteCompany("南京办事处");
comp1->add(comp1, InitHRDepartment("南京办事处人力资源部"));
comp1->add(comp1, InitFinanceDepartment("南京办事处财务部"));
comp->add(comp, comp1);
Company *comp2 = InitConcreteCompany("杭州办事处");
comp2->add(comp2, InitHRDepartment("杭州办事处人力资源部"));
comp2->add(comp2, InitFinanceDepartment("杭州办事处财务部"));
comp->add(comp, comp2);
printf("结构图:\n");
root->display(root, 0);
printf("职责:\n");
root->lineOfDuty(root);
return 0;
}
客户端打印:
text
结构图:
北京总公司
-总公司人力资源部
-总公司财务部
-上海华东分公司
--华东分公司人力资源部
--华东分公司财务部
--南京办事处
---南京办事处人力资源部
---南京办事处财务部
--杭州办事处
---杭州办事处人力资源部
---杭州办事处财务部
职责:
总公司人力资源部 员工招聘培训管理
总公司财务部 公司财务收支管理
华东分公司人力资源部 员工招聘培训管理
华东分公司财务部 公司财务收支管理
南京办事处人力资源部 员工招聘培训管理
南京办事处财务部 公司财务收支管理
杭州办事处人力资源部 员工招聘培训管理
杭州办事处财务部 公司财务收支管理
UML图

总结
- 组合模式使用场景?
"当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。"