设计模式(策略,观察者,单例,工厂方法)

文章目录

    • [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包含SubjectConcreteObserver包含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语言核心特性,完全可以在过程式语言的范式中构建出灵活、可维护、可扩展的高质量代码架构。

相关推荐
宁静致远20216 小时前
【C++设计模式】第三篇:观察者模式(别名:发布-订阅模式、模型-视图模式、源-监听器模式)
c++·观察者模式·设计模式
User_芊芊君子9 小时前
【Java】设计模式——单例、工厂、代理模式
java·设计模式·代理模式
YA33312 小时前
java设计模式二、工厂
java·开发语言·设计模式
烛阴1 天前
【TS 设计模式完全指南】从零到一:掌握TypeScript建造者模式,让你的对象构建链式优雅
javascript·设计模式·typescript
yvya_1 天前
常见设计模式详解
设计模式
至此流年莫相忘1 天前
设计模式:模板方法模式
java·开发语言·设计模式
o0向阳而生0o1 天前
100、23种设计模式之适配器模式(9/23)
设计模式·适配器模式
将编程培养成爱好1 天前
C++ 设计模式《外卖菜单展示》
c++·设计模式
TechNomad2 天前
设计模式:状态模式(State Pattern)
设计模式·状态模式