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图

总结

  • 组合模式使用场景?
    "当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。"
相关推荐
进击的小头10 小时前
设计模式组合应用:智能硬件控制系统
c语言·设计模式
小码过河.12 小时前
设计模式——迭代器模式
设计模式·迭代器模式
琹箐1 天前
设计模式——观察者模式
观察者模式·设计模式
小码过河.1 天前
设计模式——责任链模式
设计模式·责任链模式
sg_knight1 天前
抽象工厂模式(Abstract Factory)
java·python·设计模式·抽象工厂模式·开发
短剑重铸之日1 天前
《设计模式》第二篇:单例模式
java·单例模式·设计模式·懒汉式·恶汉式
J_liaty1 天前
23种设计模式一抽象工厂模式‌
设计模式·抽象工厂模式
短剑重铸之日1 天前
《设计模式》第一篇:初识
java·后端·设计模式
Cher ~2 天前
23种设计模式
开发语言·c++·设计模式
酉鬼女又兒2 天前
java三个工厂设计模式
java·开发语言·设计模式