C 语言设计模式(结构型)

文章目录

  • 代理模式
    • 场景
    • 示例
  • 门面模式
    • 场景
    • 示例
  • 桥接模式
    • 场景
    • 示例
  • 适配器模式
    • 场景
    • 示例
  • 外观模式
    • 场景
    • 示例
  • 享元模式
    • 场景
    • 示例
  • 装饰器模式
    • 场景
    • 示例
  • 组合模式
    • 场景
    • 示例

代理模式

C语言中,代理模式通常用于实现对象的间接访问。代理模式是一种结构型设计模式,它允许你提供一个代理对象来控制对另一个对象的访问。这种类型的设计模式属于结构型模式,因为它创建了对象的代理,以控制对它们的访问。

  1. 抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样代理就可以用来代替真实主题。

2。 真实主题(Real Subject):定义了实际对象,代理对象是对它的一个引用。代理模式中的大部分工作都由真实主题完成,而代理主要负责将请求传递给真实主题。

  1. 代理主题(Proxy):保存一个引用使得代理可以访问真实主题,并提供与真实主题相同的接口,这样客户端就无需知道代理和真实主题的区别。

代理模式通常被用来解决以下一些问题:

  • 远程代理(Remote Proxy):在不同的地址空间中代表一个对象。这种情况下,代理对象负责将请求和参数进行编码,并向远程对象发送请求。

  • 虚拟代理(Virtual Proxy):用于按需创建昂贵对象,这样只有在真正需要时才会实例化真实对象。例如,一个图像浏览器可能只在需要显示图像时才会加载真正的图像数据。

  • 保护代理(Protection Proxy):用于控制对对象的访问,可以根据访问权限拒绝调用者的请求。

场景

  1. 文件访问控制:在一个需要对文件进行访问控制的系统中,可以使用代理模式来创建一个文件访问代理,以控制对文件的读取和写入操作,实现对文件访问的安全管理。

  2. 网络通信:在网络通信中,可以使用代理模式来创建网络通信代理,以控制对网络资源的访问,实现网络通信的安全验证、加密解密等功能。

  3. 资源管理:在需要管理共享资源的系统中,可以使用代理模式来创建资源管理代理,以控制对资源的访问,实现资源的共享和保护。

  4. 缓存管理:在需要缓存数据以提高性能的系统中,可以使用代理模式来创建缓存代理,以控制对数据的访问,实现数据的缓存和更新策略。

  5. 权限管理:在需要对用户权限进行管理的系统中,可以使用代理模式来创建权限管理代理,以控制对资源的访问,实现对用户权限的控制和管理。

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 定义抽象主题接口
struct File {void (*read)(struct File *);void (*write)(struct File *, const char *);
};// 定义真实主题
struct RealFile {struct File file; // 继承自抽象主题char *name;
};// 真实主题的读操作实现
void real_read(struct File *file) {struct RealFile *realFile = (struct RealFile *)file;printf("读取文件:%s\n", realFile->name);
}// 真实主题的写操作实现
void real_write(struct File *file, const char *data) {struct RealFile *realFile = (struct RealFile *)file;printf("写入文件:%s\n", realFile->name);printf("数据:%s\n", data);
}// 定义代理主题
struct ProxyFile {struct File file; // 继承自抽象主题struct RealFile *realFile;
};// 代理主题的读操作实现
void proxy_read(struct File *file) {struct ProxyFile *proxyFile = (struct ProxyFile *)file;printf("代理请求读取...\n");proxyFile->realFile->file.read(&(proxyFile->realFile->file));
}// 代理主题的写操作实现
void proxy_write(struct File *file, const char *data) {struct ProxyFile *proxyFile = (struct ProxyFile *)file;printf("代理请求写入...\n");proxyFile->realFile->file.write(&(proxyFile->realFile->file), data);
}int main() {// 创建真实文件对象struct RealFile *realFile = malloc(sizeof(struct RealFile));realFile->name = strdup("example.txt");realFile->file.read = real_read;realFile->file.write = real_write;// 创建文件代理对象struct ProxyFile *proxyFile = malloc(sizeof(struct ProxyFile));proxyFile->realFile = realFile;proxyFile->file.read = proxy_read;proxyFile->file.write = proxy_write;// 使用代理对象读写文件proxyFile->file.read(&(proxyFile->file));proxyFile->file.write(&(proxyFile->file), "Hello, Proxy Pattern!");// 释放内存free(realFile->name);free(realFile);free(proxyFile);return 0;
}
  • 输出结果
代理请求读取...
读取文件:example.txt
代理请求写入...
写入文件:example.txt
数据:Hello, Proxy Pattern!

门面模式

C语言中,门面模式(Facade Pattern)是一种常见的设计模式,用于提供一个统一的接口,以简化一组复杂系统的使用。门面模式隐藏了系统的复杂性,为客户端提供了一个更简单的接口,从而使客户端不需要了解系统的内部实现细节。

  1. 门面(Facade):提供了一个简单的接口,用于将客户端的请求委派给系统内部的各个子系统。

  2. 子系统(Subsystems):实现了系统的各个功能,但是这些功能对客户端来说可能过于复杂。门面模式通过封装这些子系统,提供了一个更简单的接口供客户端使用。

场景

  1. 复杂系统的简化接口:当一个系统由多个子系统组成,而客户端需要使用的功能较少或者不需要了解系统内部的复杂结构时,门面模式可以提供一个简化的接口,隐藏系统的复杂性,使得客户端更容易使用。

  2. 库或框架的封装:当开发一个库或框架时,为了提供更加友好的接口给用户使用,可以使用门面模式来封装库或框架的内部实现细节,使得用户只需调用简单的接口即可完成复杂的操作。

  3. 遗留系统的接口升级:当需要对一个遗留系统进行接口升级或者重构时,可以使用门面模式来封装原有的接口,提供一个向后兼容的接口给客户端使用,从而减少对客户端的影响。

  4. 跨平台开发:当需要在不同平台上开发应用程序时,可以使用门面模式来封装不同平台的差异,提供一个统一的接口给客户端使用,从而简化开发工作。

  5. 系统的分层设计:当一个系统被分为多个层次,每个层次都有不同的责任时,可以使用门面模式来定义每个层次的门面,使得每个层次都可以独立于其他层次进行开发和测试。

示例

#include <stdio.h>// 子系统1:CPU
struct CPU {void (*run)(struct CPU *);
};// 子系统1的操作实现
void cpu_run(struct CPU *cpu) {printf("CPU开始运行\n");
}// 子系统2:内存
struct Memory {void (*load)(struct Memory *);
};// 子系统2的操作实现
void memory_load(struct Memory *memory) {printf("内存加载数据\n");
}// 子系统3:硬盘
struct HardDrive {void (*read)(struct HardDrive *);
};// 子系统3的操作实现
void harddrive_read(struct HardDrive *hardDrive) {printf("硬盘读取数据\n");
}// 门面:计算机
struct Computer {struct CPU cpu;struct Memory memory;struct HardDrive hardDrive;void (*start)(struct Computer *);
};// 门面的操作实现
void computer_start(struct Computer *computer) {printf("计算机启动中...\n");// 启动计算机时,依次启动CPU、内存和硬盘computer->cpu.run(&(computer->cpu));computer->memory.load(&(computer->memory));computer->hardDrive.read(&(computer->hardDrive));printf("计算机启动完成\n");
}int main() {// 创建计算机对象struct Computer computer;// 初始化计算机对象的各个子系统computer.cpu.run = cpu_run;computer.memory.load = memory_load;computer.hardDrive.read = harddrive_read;// 初始化计算机对象的启动函数computer.start = computer_start;// 使用门面模式启动计算机computer.start(&computer);return 0;
}
  • 输出结果
计算机启动中...
CPU开始运行
内存加载数据
硬盘读取数据
计算机启动完成

桥接模式

C语言中,桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立变化。桥接模式通过将抽象部分与实现部分进行解耦,允许它们可以独立地进行变化,从而提高了系统的灵活性和可扩展性。

  1. 抽象部分(Abstraction):定义了抽象类接口,维护一个指向实现部分的引用。

  2. 扩展抽象部分(Refined Abstraction):继承自抽象部分,扩展了抽象类接口,增加了新的功能或行为。

  3. 实现部分(Implementor):定义了实现类接口,提供了实现类的基本操作。

  4. 具体实现部分(Concrete Implementor):实现了实现部分接口的具体类。

场景

  1. 多维度变化的系统:当一个系统有多个维度的变化,而且这些维度都需要独立变化时,可以使用桥接模式将不同维度的变化进行解耦。例如,一个图形界面系统中,可能有多种不同的绘制方式和多种不同的控件类型,可以使用桥接模式将不同的绘制方式和控件类型进行解耦,使得系统更加灵活。

  2. 运行时切换实现:当一个系统需要在运行时动态地选择不同的实现时,可以使用桥接模式。例如,一个网络通信库可能需要支持多种不同的网络协议,可以使用桥接模式将不同的网络协议的实现进行解耦,使得系统可以在运行时动态地选择不同的网络协议。

  3. 多平台支持:当一个系统需要支持多个不同的平台时,可以使用桥接模式将不同平台的实现进行解耦。例如,一个图形库可能需要支持在Windows、Linux和macOS等多个平台上运行,可以使用桥接模式将不同平台的图形接口进行解耦,使得系统更易于扩展和维护。

  4. 数据库驱动程序:在数据库访问层中,不同的数据库可能有不同的实现方式,可以使用桥接模式将不同数据库的实现进行解耦,使得系统可以在运行时动态地选择不同的数据库实现。

示例

#include <stdio.h>// 实现部分接口
struct Implementor {void (*operationImpl)(void); // 操作实现函数指针
};// 具体实现部分A
void operationImplA() {printf("具体实现部分A的操作\n");
}// 具体实现部分B
void operationImplB() {printf("具体实现部分B的操作\n");
}// 抽象部分
struct Abstraction {struct Implementor *impl; // 实现部分指针void (*operation)(struct Abstraction *); // 操作函数
};// 扩展抽象部分
void operation(struct Abstraction *abstraction) {abstraction->impl->operationImpl(); // 调用实现部分的操作
}int main() {// 创建具体实现部分A的对象struct Implementor implA;implA.operationImpl = operationImplA;// 创建具体实现部分B的对象struct Implementor implB;implB.operationImpl = operationImplB;// 创建抽象部分对象,关联具体实现部分Astruct Abstraction abstractionA;abstractionA.impl = &implA;abstractionA.operation = operation;// 调用抽象部分的操作,实际执行具体实现部分A的操作abstractionA.operation(&abstractionA);// 创建抽象部分对象,关联具体实现部分Bstruct Abstraction abstractionB;abstractionB.impl = &implB;abstractionB.operation = operation;// 调用抽象部分的操作,实际执行具体实现部分B的操作abstractionB.operation(&abstractionB);return 0;
}
  • 输出结果
具体实现部分A的操作
具体实现部分B的操作

适配器模式

C语言中,适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的类能够一起工作。适配器模式通过将一个类的接口转换成客户端所期望的另一个接口,从而使得原本由于接口不兼容而不能一起工作的类可以一起工作。

  1. 目标接口(Target):客户端期望的接口,适配器模式通过适配器将被适配者的接口转换成目标接口。

  2. 被适配者(Adaptee):需要被适配的类,它的接口与目标接口不兼容。

  3. 适配器(Adapter):实现了目标接口,并包含一个被适配者的对象,它将客户端的请求转发给被适配者,并进行适配。

场景

  1. 使用现有库或框架:当需要使用一个已经存在的库或框架,但它的接口与系统的其他部分不兼容时,可以使用适配器模式将它的接口转换成系统所期望的接口。

  2. 跨平台开发:在跨平台开发中,不同平台上的API可能存在差异,导致代码无法跨平台使用。可以使用适配器模式将不同平台上的API接口进行统一,使得代码可以跨平台使用。

  3. 日志系统的适配:当系统需要更换日志系统时,可以使用适配器模式将新的日志系统的接口适配成原有日志系统的接口,使得系统可以无缝切换日志系统而不需要修改大量代码。

  4. 数据库访问的适配:当系统需要支持多种不同的数据库时,可以使用适配器模式将不同数据库的访问接口适配成统一的接口,使得系统可以使用相同的代码访问不同的数据库。

  5. 设备驱动的适配:当系统需要支持多种不同的硬件设备时,可以使用适配器模式将不同设备的驱动接口适配成统一的接口,使得系统可以使用相同的代码操作不同的设备。

示例

#include <stdio.h>// 目标接口
struct Target {void (*request)(struct Target *);
};// 被适配者
struct Adaptee {void (*specificRequest)(struct Adaptee *);
};// 被适配者的特定请求实现
void specificRequest(struct Adaptee *adaptee) {printf("被适配者的特定请求\n");
}// 适配器
struct Adapter {struct Target target; // 目标接口struct Adaptee adaptee; // 被适配者
};// 适配器的请求实现
void request(struct Target *target) {// 将目标接口的请求转发给被适配者的特定请求struct Adapter *adapter = (struct Adapter *)target;adapter->adaptee.specificRequest(&(adapter->adaptee));
}int main() {// 创建被适配者对象struct Adaptee adaptee;adaptee.specificRequest = specificRequest;// 创建适配器对象struct Adapter adapter;adapter.target.request = request;adapter.adaptee = adaptee;// 使用适配器调用目标接口adapter.target.request(&(adapter.target));return 0;
}
  • 输出结果
被适配者的特定请求

外观模式

外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,使得子系统更容易使用。

在C语言中,外观模式可以帮助简化复杂系统的使用,隐藏系统的复杂性,提供一个更加友好和简单的接口给客户端使用。外观模式通常包含以下几个角色:

  1. 外观(Facade):提供了一个简单的接口,用来访问子系统中的一群接口。外观模式通过将客户端的请求委派给子系统中的各个对象来实现这个接口。

  2. 子系统(Subsystems):实现了系统的各个功能,但是这些功能对客户端来说可能过于复杂。外观模式通过封装这些子系统,提供了一个更简单的接口供客户端使用。

场景

  1. 复杂系统的简化接口:当一个系统由多个子系统组成,而客户端只需使用其中的一部分功能时,可以使用外观模式将这些子系统的复杂功能进行封装,提供一个简化的接口给客户端使用。

  2. 隐藏系统的复杂性:当一个系统的内部结构复杂,而客户端不需要了解系统的内部实现细节时,可以使用外观模式隐藏系统的复杂性,提供一个更加友好和简单的接口给客户端使用。

  3. 简化接口的调用流程:当一个系统的接口调用流程比较繁琐,需要依次调用多个接口时,可以使用外观模式将这些接口调用流程进行封装,提供一个更简单和直观的接口给客户端使用。

  4. 封装遗留系统:当一个系统的接口需要向后兼容或者需要与其他系统进行集成时,可以使用外观模式封装遗留系统的接口,提供一个统一的接口给客户端使用,从而降低系统的耦合度。

  5. 统一接口规范:当一个系统需要支持多种不同的接口规范时,可以使用外观模式将这些不同的接口规范进行统一,提供一个统一的接口给客户端使用,使得客户端可以无需关心不同接口规范的细节。

示例

#include <stdio.h>// 子系统1:CPU
struct CPU {void (*run)(void); // 运行方法
};// 子系统1的具体实现
void cpu_run() {printf("CPU开始运行\n");
}// 子系统2:内存
struct Memory {void (*load)(void); // 加载方法
};// 子系统2的具体实现
void memory_load() {printf("内存加载数据\n");
}// 子系统3:硬盘
struct HardDrive {void (*read)(void); // 读取方法
};// 子系统3的具体实现
void harddrive_read() {printf("硬盘读取数据\n");
}// 外观:计算机
struct ComputerFacade {struct CPU cpu;struct Memory memory;struct HardDrive hardDrive;void (*start)(void); // 启动方法
};// 外观的实现
void start() {printf("计算机启动中...\n");// 启动计算机时,依次启动CPU、内存和硬盘cpu_run();memory_load();harddrive_read();printf("计算机启动完成\n");
}int main() {// 创建外观对象struct ComputerFacade computerFacade;// 初始化外观对象的各个子系统computerFacade.cpu.run = cpu_run;computerFacade.memory.load = memory_load;computerFacade.hardDrive.read = harddrive_read;// 初始化外观对象的启动方法computerFacade.start = start;// 使用外观模式启动计算机computerFacade.start();return 0;
}
  • 输出结果
计算机启动中...
CPU开始运行
内存加载数据
硬盘读取数据
计算机启动完成

享元模式

享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在通过共享对象来最大限度地减少内存使用和提高性能。在享元模式中,将对象的状态分为内部状态(Intrinsic State)和外部状态(Extrinsic State),其中内部状态可以被多个对象共享,而外部状态需要根据场景而变化。

  1. 享元工厂(Flyweight Factory):负责创建和管理享元对象,通过共享已创建的享元对象来减少对象的创建数量。

  2. 享元接口(Flyweight Interface):定义了享元对象的接口,包括设置外部状态的方法等。

  3. 具体享元对象(Concrete Flyweight):实现了享元接口,包含了内部状态,并对外部状态进行了处理。

  4. 客户端(Client):通过享元工厂获取享元对象,并设置外部状态,实现具体的业务逻辑。

场景

  1. 大量对象的共享:当系统中存在大量相似对象,并且这些对象可以共享部分状态时,可以使用享元模式来减少对象的创建数量,降低内存消耗。

  2. 对象的状态可分为内部状态和外部状态:当一个对象的状态可以分为内部状态(Intrinsic State)和外部状态(Extrinsic State),并且内部状态可以被多个对象共享时,可以使用享元模式来共享内部状态,减少对象的创建数量。

  3. 需要缓存对象的场景:当系统需要缓存一些常用的对象,并且这些对象可以共享部分状态时,可以使用享元模式来缓存这些对象,提高系统的性能。

  4. 系统需要保持独立的对象:当系统需要保持独立的对象,但又希望共享相同的状态时,可以使用享元模式来共享这些状态,同时保持对象的独立性。

  5. 需要减少系统中对象的数量:当系统中存在大量相似对象,并且这些对象可以共享部分状态时,可以使用享元模式来减少对象的创建数量,提高系统的性能和资源利用率。

示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 享元对象结构体
typedef struct {char *name; // 内部状态int size;   // 外部状态
} Flyweight;// 享元工厂
Flyweight *get_flyweight(char *name) {static Flyweight *flyweights[10] = {NULL}; // 预先创建的享元对象数组static int index = 0;// 查找现有的享元对象for (int i = 0; i < index; i++) {if (strcmp(flyweights[i]->name, name) == 0) {return flyweights[i];}}// 创建新的享元对象Flyweight *flyweight = (Flyweight *)malloc(sizeof(Flyweight));flyweight->name = strdup(name);flyweights[index++] = flyweight;return flyweight;
}// 客户端使用享元对象
void client(char *name, int size) {Flyweight *flyweight = get_flyweight(name);printf("对象名:%s,大小:%d\n", flyweight->name, size);
}int main() {// 客户端使用享元对象client("object1", 10);client("object2", 20);client("object1", 30); // 复用现有的享元对象client("object3", 15);client("object2", 25); // 复用现有的享元对象return 0;
}
  • 输出结果
对象名:object1,大小:10
对象名:object2,大小:20
对象名:object1,大小:30
对象名:object3,大小:15
对象名:object2,大小:25

装饰器模式

装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向现有对象动态地添加新功能,同时又不改变其结构。装饰器模式通过创建一个包装器(Wrapper),将被装饰的对象进行包装,从而可以在运行时动态地添加新的功能。

  1. 组件接口(Component Interface):定义了被装饰对象和装饰器共同实现的接口。

  2. 具体组件(Concrete Component):实现了组件接口的具体对象,它是被装饰的对象。

  3. 装饰器(Decorator):实现了组件接口,并包含了一个指向被装饰对象的指针,它可以动态地添加新的功能。

  4. 具体装饰器(Concrete Decorator):实现了装饰器接口的具体对象,用于添加新的功能。

场景

  1. 动态添加功能:当需要在不修改对象的基本结构的情况下,动态地添加额外的功能时,可以使用装饰器模式。这样可以避免修改现有的代码,使得系统更加灵活和可扩展。

  2. 多个功能的组合:当需要对对象的功能进行组合和排列,以满足不同的需求时,可以使用装饰器模式。通过组合不同的装饰器,可以实现不同的功能组合,而无需创建大量的子类。

  3. 功能的分层扩展:当需要对对象的功能进行分层扩展,以便于复用和维护时,可以使用装饰器模式。通过创建不同层次的装饰器,可以实现对对象功能的分层扩展,从而使得系统更易于理解和维护。

  4. 功能的独立扩展:当需要对对象的部分功能进行独立扩展和修改时,可以使用装饰器模式。通过创建独立的装饰器,可以实现对对象的部分功能进行扩展,而不影响其他功能的使用。

  5. 动态改变对象的行为:当需要动态地改变对象的行为,以适应不同的场景时,可以使用装饰器模式。通过在运行时动态地装饰对象,可以实现对对象行为的动态改变,从而提高系统的灵活性和可定制性。

示例

#include <stdio.h>// 组件接口
typedef struct {void (*operation)(void);
} Component;// 具体组件
typedef struct {Component component;
} ConcreteComponent;// 具体组件的操作实现
void concrete_operation(void) {printf("执行具体组件的操作\n");
}// 装饰器
typedef struct {Component component;void (*decorate)(Component *);
} Decorator;// 具体装饰器1
typedef struct {Decorator decorator;
} ConcreteDecorator1;// 具体装饰器1的装饰方法
void decorate1(Component *component) {printf("在具体装饰器1中添加功能1\n");component->operation();
}// 具体装饰器2
typedef struct {Decorator decorator;
} ConcreteDecorator2;// 具体装饰器2的装饰方法
void decorate2(Component *component) {printf("在具体装饰器2中添加功能2\n");component->operation();
}int main() {// 创建具体组件ConcreteComponent concreteComponent;concreteComponent.component.operation = concrete_operation;// 创建具体装饰器1,并装饰具体组件ConcreteDecorator1 concreteDecorator1;concreteDecorator1.decorator.component = concreteComponent.component;concreteDecorator1.decorator.decorate = decorate1;concreteDecorator1.decorator.decorate(&(concreteDecorator1.decorator.component));// 创建具体装饰器2,并装饰具体组件ConcreteDecorator2 concreteDecorator2;concreteDecorator2.decorator.component = concreteComponent.component;concreteDecorator2.decorator.decorate = decorate2;concreteDecorator2.decorator.decorate(&(concreteDecorator2.decorator.component));return 0;
}
  • 输出结果
在具体装饰器1中添加功能1
执行具体组件的操作
在具体装饰器2中添加功能2
执行具体组件的操作

组合模式

组合模式(Composite Pattern)是一种结构型设计模式,它允许将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式让客户端以统一的方式处理单个对象以及对象组合,从而使得客户端无需区分对象和对象组合。

  1. 组件接口(Component Interface):定义了组合中所有对象共同的接口,以便于客户端统一处理对象和对象组合。

  2. 叶子组件(Leaf Component):表示组合中的叶子节点,它实现了组件接口,但是没有子节点。

  3. 复合组件(Composite Component):表示组合中的复合节点,它实现了组件接口,并且包含了子节点。

场景

  1. 文件系统:文件系统中的目录和文件可以使用组合模式来表示。目录可以看作是复合组件,它包含了多个文件或目录(叶子组件和复合组件),而文件可以看作是叶子组件,它不包含其他文件或目录。

  2. 图形用户界面(GUI):图形用户界面中的控件和容器可以使用组合模式来表示。控件可以看作是叶子组件,它显示具体的内容或功能,而容器可以看作是复合组件,它包含了多个控件(叶子组件和复合组件)。

  3. 组织架构:组织架构中的部门和员工可以使用组合模式来表示。部门可以看作是复合组件,它包含了多个员工(叶子组件和复合组件),而员工可以看作是叶子组件,它不包含其他员工。

  4. 菜单系统:菜单系统中的菜单项和菜单可以使用组合模式来表示。菜单项可以看作是叶子组件,它表示一个具体的功能或选项,而菜单可以看作是复合组件,它包含了多个菜单项(叶子组件和复合组件)。

  5. 物品组织:在游戏开发中,物品的组织结构可以使用组合模式来表示。物品可以看作是叶子组件,它表示一个具体的物品,而物品箱或背包可以看作是复合组件,它包含了多个物品(叶子组件和复合组件)。

示例

#include <stdio.h>// 定义组件类型
typedef enum {LEAF,       // 叶子节点COMPOSITE   // 复合节点
} NodeType;// 定义组件结构体
typedef struct Node {NodeType type;          // 节点类型:叶子节点或复合节点char *name;             // 节点名称union {struct Node *child; // 叶子节点指向NULL,复合节点指向子节点链表的头节点struct Node *next;  // 复合节点链表中的下一个节点} next;
} Node;// 打印节点名称
void print_node(Node *node) {printf("%s\n", node->name);
}// 打印树节点
void print_tree(Node *root, int depth) {if (root == NULL) return;// 打印节点名称for (int i = 0; i < depth; ++i) {printf("  ");}print_node(root);// 递归打印子节点if (root->type == COMPOSITE) {print_tree(root->next.child, depth + 1);}// 递归打印兄弟节点print_tree(root->next.next, depth);
}int main() {// 创建根节点Node root = {COMPOSITE, "根节点", .next = {NULL}};// 创建子节点Node node1 = {LEAF, "叶子节点1", .next = {NULL}};Node node2 = {LEAF, "叶子节点2", .next = {NULL}};// 创建复合节点Node composite_node = {COMPOSITE, "复合节点", .next = {.child = &node1}};// 将复合节点添加到根节点root.next.next = &composite_node;composite_node.next.next = &node2;// 打印树节点print_tree(&root, 0);return 0;
}
  • 输出结果
根节点复合节点叶子节点2叶子节点2
复合节点叶子节点2
叶子节点2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/14111.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序uniapp+django洗脚按摩足浴城消费系统springboot

原生wxml开发对Node、预编译器、webpack支持不好&#xff0c;影响开发效率和工程构建。所以都会用uniapp框架开发 前后端分离&#xff0c;后端给接口和API文档&#xff0c;注重前端,接近原生系统 使用Navicat或者其它工具&#xff0c;在mysql中创建对应名称的数据库&#xff0…

Java中字符串拼接方式

在Java编程中&#xff0c;字符串是非常基础且重要的数据类型。无论是处理用户输入、生成日志信息还是构建复杂的文本输出&#xff0c;字符串拼接都是一个不可避免的操作。本文将详细探讨几种常见的字符串拼接方式&#xff0c;并分析它们的优缺点&#xff0c;以帮助开发者选择最…

GO实名认证接口开发示例、接口集成、身份认证

翔云身份证实名认证接口&#xff0c;通过核验身份证二要素、三要素、三要素现场人像的方式&#xff0c;实时联网核验身份信息的真伪。想象一下&#xff0c;无需耗费大量的人力物力&#xff0c;只需简单几步&#xff0c;即可将翔云身份证实名认证接口集成到您的应用中。 无论是…

集成框架 -- 项目启动时创建mysql数据库结构

使用 Spring JDBC DataSource 初始化 前言正文配置Spring JDBC的DataSource初始化application.propertiesapplication.yml 使用 data.sql 进行数据库初始化application.propertiesapplication.ymlapplication.propertiesapplication.yml 前言 项目中要使用一些数据库&#xff…

xjoi题库一级二段题解(c语言版)

开根号 时间&#xff1a;0.2 空间&#xff1a;32M 题目描述&#xff1a; 输入一个整数, 求它的平方根,输出答案向下取整. 比如5√2, 16−−√4 输入格式&#xff1a; 输入一个整数 输出格式&#xff1a; 输出一个整数 样例输入1&#xff1a; 5 样例输出1&#xff1a; 2 样例输…

cn.hutool.poi.excel 实现excel导出效果 首行高度,行样式,颜色,合并单元格,例子样式

需求 接了需求&#xff0c;下载excel模版&#xff0c;本来看着还是简单的&#xff0c;然后实现起来一把泪&#xff0c;首先是使用poi&#xff0c;我查了好久&#xff0c;才实现&#xff0c;然后是我用easyexcel又实现了一遍&#xff0c;用了一个周多才实现。 这是需求&#x…

Python使用virtualenv创建虚拟环境

目录 第一步&#xff1a;安装virtualenv 第二步&#xff1a;选择一个文件夹用来放所创建的虚拟环境 第三步&#xff1a;创建虚拟环境 第四步&#xff1a;激活虚拟环境 第五步&#xff1a;退出虚拟环境 第六步&#xff1a;测试安装django 前提&#xff1a;你得有个python环…

【STL专题】深入探索C++之std::string:不止于字符串【万字详解】

欢迎来到CILMY23的博客 &#x1f3c6;本篇主题为&#xff1a;深入探索C之std::string&#xff1a;不止于字符串 &#x1f3c6;个人主页&#xff1a;CILMY23-CSDN博客 &#x1f3c6;系列专栏&#xff1a;Python | C | C语言 | 数据结构与算法 | 贪心算法 | Linux &#x1f3…

IOS手机自动化一些工具的简单有哪些?

iOS手机自动化测试或操作可以通过多种工具来实现&#xff0c;这些工具提供了丰富的功能&#xff0c;可以帮助开发者和测试人员提高效率。以下是一些简单的iOS自动化工具&#xff1a; 1. Xcode: 苹果官方提供的开发工具&#xff0c;包含了iOS应用开发、调试和自动化测试的功能。…

aardio - godking.vlistEx虚表点击表头全选、排序

新版虚表内置了名称为 DefaultCheckedImg 和 DefaultUnCheckedImg 的两张图片&#xff0c;分别为 【选择框勾选状态默认图片】 和 【选择框未勾选状态默认图片】 以下代码调用了这两张图片&#xff0c;所以请将虚表库升级为最新版。 如果使用旧版库&#xff0c;可以自行添加这…

【Python自动化测试】:Unittest单元测试与HTMLTestRunner自动生成测试用例的好帮手

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 &#x1f525;前言&#x1f680;unittest编写测试用例&#x1f680;unittest测…

六种常用设计模式

单例设计模式 单例模式指在整个系统生命周期里&#xff0c;保证一个类只能产生一个实例&#xff0c;确保该类的唯一性。 单例模式分类 单例模式可以分为懒汉式和饿汉式&#xff0c;两者之间的区别在于创建实例的时间不同&#xff1a; 懒汉式&#xff1a;指系统运行中&#…

SpringBootWeb 篇-深入了解 Mybatis 删除、新增、更新、查询的基础操作与 SQL 预编译解决 SQL 注入问题

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Mybatis 的基础操作 2.0 基础操作 - 环境准备 3.0 基础操作 - 删除操作 3.1 SQL 预编译 3.2 SQL 预编译的优势 3.3 参数占位符 4.0 基础操作 - 新增 4.1 主键返回…

Python图像处理:从基础到高级的全方位指南

目录 第一部分&#xff1a;Python图像处理基础 1.1 图像处理概念 1.2 Python图像处理常用库 1.3 实战案例&#xff1a;图像显示与保存 1.4 注意事项 第二部分&#xff1a;Python图像处理高级技巧 2.1 图像变换 2.2 图像增强 2.3 图像复原 第三部分&#xff1a;Python…

esp32s3中ap与sta模式的wps配对问题

无线路由器中的WPS是Wi-Fi Protected Setup的简称&#xff0c;中文翻译为Wi-Fi安全防护设置&#xff0c;它是由Wi-Fi安全联盟推出的一种无线加密认证方式。主要是为了简化无线局域网的安装及安全性能配置工作&#xff0c;通过这种设置&#xff0c;让无线连接更加方便和安全。省…

20232802 黄千里 2023-2024-2 《网络攻防实践》实践十一报告

20232802 2023-2024-2 《网络攻防实践》实践十一报告 1.实践过程 1.1web浏览器渗透攻击 攻击机&#xff1a;kali172.20.10.10靶机&#xff1a;win2k172.20.10.3 首先在kali中启动msfconsole 输入命令search MS06-014&#xff0c;搜索渗透攻击模块 输入use exploit/window…

终于让我找到了,你也可以学会的人工智能-机器学习教程

给大家分享一套非常棒的python机器学习课程——《AI小天才&#xff1a;让小学生轻松掌握机器学习》&#xff0c;2024年5月完结新课&#xff0c;提供配套的代码笔记软件包下载&#xff01;学完本课程&#xff0c;可以轻松掌握机器学习的全面应用&#xff0c;复杂特征工程&#x…

C# 跨线程访问UI组件,serialPort1串口接收数据

在Windows应用程序&#xff08;例如WinForms或WPF&#xff09;中&#xff0c;UI组件&#xff08;如按钮、文本框等&#xff09;都在主线程&#xff08;也称为UI线程&#xff09;上运行。当你在一个非UI线程&#xff08;例如&#xff0c;一个后台线程或者网络请求线程&#xff0…

关于新配置的adb,设备管理器找不到此设备问题

上面页面中一开始没有找到此android设备&#xff0c; 可能是因为我重新配置的adb和设备驱动&#xff0c; 只把adb配置了环境变量&#xff0c;驱动没有更新到电脑中&#xff0c; 点击添加驱动&#xff0c; 选择路径&#xff0c;我安装时都放在了SDK下面&#xff0c;可以尝试…

SpringBoot 实现 RAS+AES 自动接口解密

一、讲个事故 接口安全老生常谈了 过年之前做了过一款飞机大战的H5小游戏&#xff0c;里面无限模式-需要保存用户的积分&#xff0c;因为使用的Body传参&#xff0c;参数是可见的。 为了接口安全我&#xff0c;我和前端约定了传递参数是&#xff1a;用户无限模式的积分“我们…