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

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代码实现

//--- 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代码实现(简化版)

//--- 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代码实现

//--- 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代码实现

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

posted @ 2025-09-10 17:16  yfceshi  阅读(159)  评论(0)    收藏  举报