文章目录
-
- [1. 设计模式核心概念与C语言实现基础](#1. 设计模式核心概念与C语言实现基础)
- [2. 常用设计模式详解](#2. 常用设计模式详解)
-
- [模式一:策略模式(Strategy Pattern)](#模式一:策略模式(Strategy Pattern))
- [模式二:观察者模式(Observer Pattern)](#模式二:观察者模式(Observer Pattern))
- [模式三:单例模式(Singleton Pattern)](#模式三:单例模式(Singleton Pattern))
- [模式四:工厂方法模式(Factory Method Pattern)](#模式四:工厂方法模式(Factory Method Pattern))
1. 设计模式核心概念与C语言实现基础
设计模式是一套被反复使用、多数人知晓、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中一些不断重复发生的问题,以及该问题的解决方案的核心。
在C语言中实现设计模式,主要依赖于以下技术来模拟面向对象特性:
- 结构体(Structs): 用于封装数据,模拟类的属性。
- 函数指针(Function Pointers) : 用于封装行为,模拟类的方法。这是实现多态(Polymorphism) 和继承(Inheritance) 的关键。
- 头文件(.h)和源文件(.c) : 用于实现封装(Encapsulation) 和信息隐藏。头文件暴露结构体和公共函数接口,源文件隐藏私有数据和实现细节。
void指针(void*)
: 用于实现泛型编程,处理未知类型的数据。
2. 常用设计模式详解
以下选择四个在系统级 、嵌入式 、中间件等C语言主导领域非常实用的模式。
模式一:策略模式(Strategy Pattern)
1、意图
定义一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。
2、UML图示
uses Context -Strategy *strategy +setStrategy(Strategy*) +executeStrategy() <<interface>> Strategy +execute() ConcreteStrategyA +execute() ConcreteStrategyB +execute()
Context
:持有一个策略对象的引用,用接口与策略对象交互。Strategy
:策略接口,声明了所有具体策略的通用方法(execute
)。ConcreteStrategy
:实现了策略接口的具体算法。
3、C代码实现
c
//--- strategy.h ---
// 声明策略接口(用结构体模拟)
typedef struct Strategy Strategy;
struct Strategy {
void (*execute)(void); // 函数指针,代表算法接口
};
// 上下文类,用于连接策略
typedef struct Context Context;
struct Context {
Strategy *strategy;
void (*setStrategy)(Context *, Strategy *);
void (*executeStrategy)(Context *);
};
// 上下文的构造函数
Context *context_new();
//--- strategy.c ---
#include <stdio.h>
#include <stdlib.h>
#include "strategy.h"
// 上下文的方法实现
static void setStrategy(Context *self, Strategy *strategy) {
self->strategy = strategy;
}
static void executeStrategy(Context *self) {
if (self->strategy && self->strategy->execute) {
self->strategy->execute(); // 委托给具体策略
} else {
printf("No strategy set.\n");
}
}
// 上下文构造函数
Context *context_new() {
Context *ctx = (Context *)malloc(sizeof(Context));
ctx->strategy = NULL;
ctx->setStrategy = setStrategy;
ctx->executeStrategy = executeStrategy;
return ctx;
}
//--- concrete_strategy_a.c ---
#include <stdio.h>
#include "strategy.h"
// 具体策略A的实现
static void execute_a(void) {
printf("Executing strategy A: Quick sort algorithm.\n");
}
// 具体策略A的"构造函数",创建一个策略对象
Strategy *strategy_a_new() {
Strategy *s = (Strategy *)malloc(sizeof(Strategy));
s->execute = execute_a; // 将函数指针指向具体实现
return s;
}
//--- concrete_strategy_b.c ---
// ... 类似地实现策略B
static void execute_b(void) {
printf("Executing strategy B: Merge sort algorithm.\n");
}
Strategy *strategy_b_new() {
Strategy *s = (Strategy *)malloc(sizeof(Strategy));
s->execute = execute_b;
return s;
}
//--- main.c ---
int main() {
Context *ctx = context_new();
Strategy *strategyA = strategy_a_new();
Strategy *strategyB = strategy_b_new();
// 使用策略A
ctx->setStrategy(ctx, strategyA);
ctx->executeStrategy(ctx); // 输出: Executing strategy A...
// 动态切换为策略B
ctx->setStrategy(ctx, strategyB);
ctx->executeStrategy(ctx); // 输出: Executing strategy B...
// 释放内存...
return 0;
}
4、技术与内容
- 技术 : 使用结构体嵌套函数指针 来模拟接口和多态。
Context
依赖于抽象的Strategy
接口,而非具体实现。 - 内容 : 遵循开闭原则 (对扩展开放,对修改关闭)。添加新算法(新策略)只需创建新的
ConcreteStrategy
,而无需修改Context
的代码。常用于算法选择、文件格式转换、日志策略等场景。
模式二:观察者模式(Observer Pattern)
1、意图
定义对象间的一种一对多的依赖关系,当一个对象(主题)的状态发生改变时,所有依赖于它的对象(观察者)都得到通知并被自动更新。
2、UML图示
notifies observes Subject -observers : List<Observer> +attach(Observer*) +detach(Observer*) +notify() ConcreteSubject -state : int +getState() : int +setState(int) <<interface>> Observer +update() ConcreteObserver -subject : Subject* +update()
3、C代码实现(简化版)
c
//--- observer.h ---
typedef struct Observer Observer;
typedef struct Subject Subject;
// 观察者接口
struct Observer {
void (*update)(Observer *self, int state); // 通知函数
};
// 主题基类
struct Subject {
Observer *observers[10]; // 简单的观察者数组(实际应用可用链表)
int count;
void (*attach)(Subject *, Observer *);
void (*detach)(Subject *, Observer *);
void (*notify)(Subject *);
};
//--- subject.c ---
#include "observer.h"
#include <stdio.h>
static void attach(Subject *self, Observer *obs) {
if (self->count < 10) {
self->observers[self->count++] = obs;
}
}
static void detach(Subject *self, Observer *obs) { /* ... 从数组中移除 ... */ }
static void notify(Subject *self) {
for (int i = 0; i < self->count; i++) {
if (self->observers[i] && self->observers[i]->update) {
// 这里需要知道状态,通常ConcreteSubject会重写notify
// 为了简化,我们假设传递一个虚拟状态值0
self->observers[i]->update(self->observers[i], 0);
}
}
}
// 主题的"基类"构造函数
Subject *subject_new() {
Subject *sub = (Subject *)malloc(sizeof(Subject));
sub->count = 0;
sub->attach = attach;
sub->detach = detach;
sub->notify = notify;
return sub;
}
//--- concrete_subject.c ---
// 具体主题,拥有状态
typedef struct {
Subject base; // 模拟"继承",Base放在第一个成员,可以实现强制转换
int state;
} ConcreteSubject;
ConcreteSubject *concrete_subject_new() {
ConcreteSubject *cs = (ConcreteSubject *)malloc(sizeof(ConcreteSubject));
cs->base = *subject_new(); // 初始化基类部分
cs->state = 0;
return cs;
}
// 重写notify?或者提供setState方法,在setState中调用notify
void concrete_subject_set_state(ConcreteSubject *self, int state) {
self->state = state;
self->base.notify((Subject *)self); // 通知所有观察者
}
//--- concrete_observer.c ---
typedef struct {
Observer base;
ConcreteSubject *subject; // 观察者需要知道它所观察的主题
} ConcreteObserver;
static void update(Observer *self, int state) {
ConcreteObserver *co = (ConcreteObserver *)self; // 获取包含自己的大结构体
// 从主题获取真实状态
int actual_state = co->subject->state;
printf("Observer %p: Subject's state changed to %d\n", (void*)self, actual_state);
}
ConcreteObserver *concrete_observer_new(ConcreteSubject *sub) {
ConcreteObserver *co = (ConcreteObserver *)malloc(sizeof(ConcreteObserver));
co->base.update = update; // 实现接口
co->subject = sub;
sub->base.attach((Subject *)sub, (Observer *)co); // 注册自己
return co;
}
//--- main.c ---
int main() {
ConcreteSubject *subject = concrete_subject_new();
ConcreteObserver *obs1 = concrete_observer_new(subject);
ConcreteObserver *obs2 = concrete_observer_new(subject);
// 改变主题状态,观察者会自动被通知
concrete_subject_set_state(subject, 10);
concrete_subject_set_state(subject, 20);
return 0;
}
4、技术与内容
- 技术 : 使用组合 和函数指针 。主题维护一个观察者列表。关键技巧是结构体嵌套 (
ConcreteSubject
包含Subject
,ConcreteObserver
包含Observer
)来实现一种形式的继承和向上转换。 - 内容 : 实现了发布-订阅 机制,彻底解耦了主题和观察者。主题不知道观察者的具体类,只知道它们实现了
Observer
接口。广泛应用于GUI事件处理、数据监控、消息队列等。
模式三:单例模式(Singleton Pattern)
1、意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2、UML图示
Singleton -static instance : Singleton* -Singleton() +static getInstance()
- 私有构造函数防止外部
new
。 - 静态变量
instance
持有唯一实例。 - 静态方法
getInstance()
控制实例的创建和访问。
3、C代码实现
c
//--- singleton.h ---
typedef struct Singleton Singleton;
// 获取单例实例的全局函数
Singleton *singleton_get_instance(void);
// 某个业务方法
void singleton_some_business_operation(Singleton *self);
//--- singleton.c ---
#include <stdio.h>
#include <stdlib.h>
// 定义单例结构体(可以包含各种数据)
struct Singleton {
int some_value;
// ... other data
};
// 静态局部变量,在第一次函数调用时初始化,并持续存在
static Singleton *instance = NULL;
Singleton *singleton_get_instance(void) {
if (instance == NULL) {
// 首次调用,创建实例
instance = (Singleton *)malloc(sizeof(Singleton));
instance->some_value = 42; // 初始化数据
printf("Singleton instance created.\n");
}
return instance;
}
void singleton_some_business_operation(Singleton *self) {
printf("Singleton operation called. Value is %d\n", self->some_value);
}
//--- main.c ---
int main() {
// Singleton s; // 错误:结构体是不完整类型,无法在外部创建
// Singleton *s = malloc(sizeof(Singleton)); // 可以但不合规,破坏了模式
// 正确获取实例的方式
Singleton *s1 = singleton_get_instance();
Singleton *s2 = singleton_get_instance();
if (s1 == s2) {
printf("Both pointers point to the same instance.\n");
}
singleton_some_business_operation(s1);
return 0;
}
4、技术与内容
- 技术 : 使用静态局部变量 和静态全局函数 来控制实例的创建。通过不完整类型(在.h中只声明struct Singleton,在.c中才定义它)来实现信息隐藏,防止外部直接创建实例。
- 内容 : 确保一个类只有一个实例,并提供一个全局访问点。常用于需要全局管理的资源,如日志管理器、数据库连接池、应用程序配置等。注意多线程环境下的线程安全问题(上面的简单实现不是线程安全的)。
模式四:工厂方法模式(Factory Method Pattern)
1、意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
2、UML图示
creates Creator +factoryMethod() : Product +someOperation() ConcreteCreatorA +factoryMethod() : Product ConcreteCreatorB +factoryMethod() : Product <<interface>> Product +operation() ConcreteProductA +operation() ConcreteProductB +operation()
- Creator:声明工厂方法,返回Product类型对象。
- ConcreteCreator:重写工厂方法,返回一个具体的ConcreteProduct实例。
- Product:工厂方法创建的对象接口。
3、C代码实现
c
//--- product.h ---
typedef struct Product Product;
struct Product {
void (*operation)(Product *self);
};
//--- creator.h ---
typedef struct Creator Creator;
struct Creator {
// 工厂方法(函数指针),相当于一个"创建"接口
Product *(*factoryMethod)(Creator *self);
// 一个使用产品的操作
void (*someOperation)(Creator *self);
};
//--- concrete_creator_a.c ---
#include <stdio.h>
#include <stdlib.h>
#include "creator.h"
#include "product.h"
// 具体产品A
typedef struct {
Product base; // 模拟继承,使ConcreteProductA* 可被当作Product*
char specific_data[20];
} ConcreteProductA;
static void product_a_operation(Product *self) {
ConcreteProductA *pa = (ConcreteProductA *)self;
printf("ConcreteProductA operation: %s\n", pa->specific_data);
}
// 具体创建者A
typedef struct {
Creator base;
} ConcreteCreatorA;
// 工厂方法的具体实现:创建ConcreteProductA
static Product *factory_method_a(Creator *self) {
ConcreteProductA *prod = (ConcreteProductA *)malloc(sizeof(ConcreteProductA));
prod->base.operation = product_a_operation;
snprintf(prod->specific_data, 20, "Made by A");
return (Product *)prod;
}
ConcreteCreatorA *concrete_creator_a_new() {
ConcreteCreatorA *ca = (ConcreteCreatorA *)malloc(sizeof(ConcreteCreatorA));
ca->base.factoryMethod = factory_method_a;
return ca;
}
//--- main.c ---
// 客户代码只依赖于Creator和Product接口
void client_code(Creator *creator) {
printf("Client: I'm not aware of the creator's concrete class.\n");
Product *product = creator->factoryMethod(creator);
product->operation(product);
free(product); // 假设需要释放
}
int main() {
ConcreteCreatorA *creatorA = concrete_creator_a_new();
client_code((Creator *)creatorA);
// 未来可以轻松添加ConcreteCreatorB和ConcreteProductB
// ConcreteCreatorB *creatorB = concrete_creator_b_new();
// client_code((Creator *)creatorB);
free(creatorA);
return 0;
}
4、技术与内容
- 技术 : 核心是函数指针,它将对象创建的逻辑抽象成了一个接口(factoryMethod)。结合结构体嵌套,让具体的创建者决定创建何种具体的产品。
- 内容: 遵循依赖倒置原则(依赖抽象,而非具体实现)。客户代码(client_code)只与Creator和Product的抽象接口耦合,不与任何具体类耦合。这使得系统易于扩展,添加新的产品类型只需增加新的ConcreteCreator和ConcreteProduct,而无需修改现有客户代码。
模式名称 | 主要技术手段(C语言) | 核心思想与内容 | 适用场景 |
---|---|---|---|
策略模式 | 结构体 + 函数指针(接口) | 分离算法,使其可独立变化和替换 | 多种算法、策略可选的情况 |
观察者模式 | 结构体嵌套 + 函数指针 + 链表/数组 | 一对多的依赖关系,发布-订阅 | 事件驱动系统、数据监控 |
单例模式 | 静态局部变量 + 不完整类型(信息隐藏) | 控制实例数目,提供全局访问点 | 全局资源管理器 |
工厂方法模式 | 结构体嵌套 + 函数指针(工厂接口) | 将对象创建延迟到子类,解耦客户代码与具体类 | 框架设计,需要创建可扩展的对象家族 |
在C语言中应用设计模式,更多的是学习其思想精髓 而非机械照搬面向对象的实现。通过灵活运用结构体 、函数指针 、模块化编程等C语言核心特性,完全可以在过程式语言的范式中构建出灵活、可维护、可扩展的高质量代码架构。