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图

总结

  • 组合模式使用场景?
    "当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。"
相关推荐
szm02258 小时前
设计模式-
设计模式
砍光二叉树8 小时前
【设计模式】创建型-抽象工厂模式
设计模式·抽象工厂模式
砍光二叉树10 小时前
【设计模式】创建型-工厂方法模式
设计模式·工厂方法模式
我爱学习_zwj10 小时前
设计模式-2(单例模式与原型模式)
前端·javascript·设计模式
砍光二叉树11 小时前
【设计模式】创建型-单例模式
单例模式·设计模式
我爱学习_zwj11 小时前
设计模式-3(装饰器模式)
前端·设计模式·装饰器模式
文心快码BaiduComate1 天前
Comate内置模型已支持 MiniMax-M2.7!
设计模式·程序员·前端框架
console.log('npc')1 天前
Cursor,Trae,Claude Code如何协作生产出一套前后台app?
前端·人工智能·react.js·设计模式·ai·langchain·ai编程
czxyvX1 天前
C++ - 基于多设计模式下的同步&异步日志系统
c++·设计模式
蒸蒸yyyyzwd1 天前
设计模式之美学习笔记
笔记·学习·设计模式