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图

总结

  • 组合模式使用场景?
    "当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。"
相关推荐
YUEchn3 小时前
无处不在的Agent
设计模式·llm·agent
茶本无香6 小时前
设计模式之二—原型模式:灵活的对象克隆机制
java·设计模式·原型模式
GISer_Jing6 小时前
Nano Banana+LoveArt三大核心功能解析:重构AI设计全链路,让创意落地更高效
人工智能·设计模式·aigc
会员果汁7 小时前
14.设计模式-备忘录模式
设计模式·备忘录模式
xiaolyuh12317 小时前
Spring 框架 核心架构设计 深度详解
spring·设计模式·spring 设计模式
GISer_Jing1 天前
智能体工具使用、规划模式
人工智能·设计模式·prompt·aigc
GISer_Jing1 天前
AI Agent:学习与适应、模型上下文协议
人工智能·学习·设计模式·aigc
小马爱打代码1 天前
MyBatis设计模式:构建者、工厂、代理模式
设计模式·mybatis·代理模式
月明长歌1 天前
Javasynchronized 原理拆解:锁升级链路 + JVM 优化 + CAS 与 ABA 问题(完整整合版)
java·开发语言·jvm·安全·设计模式