15.设计模式-组合模式

组合模式:将对象组合成树形结构以表示'部分-整体'的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

组合模式的两种实现:

  • 透明方式:在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图

总结

  • 组合模式使用场景?
    "当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。"
相关推荐
杨充7 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充7 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充7 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充7 小时前
2.面向对象的特性
设计模式
杨充7 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充7 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充7 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯10 小时前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯19 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式