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

文章目录

    • 1. 设计模式核心概念与C语言实现基础
    • 2. 常用设计模式详解
      • 模式一:策略模式(Strategy Pattern)
      • 模式二:观察者模式(Observer Pattern)
      • 模式三:单例模式(Singleton 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代码实现

//--- 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();// 使用策略Actx->setStrategy(ctx, strategyA);ctx->executeStrategy(ctx); // 输出: Executing strategy A...// 动态切换为策略Bctx->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// 为了简化,我们假设传递一个虚拟状态值0self->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语言核心特性,完全可以在过程式语言的范式中构建出灵活、可维护、可扩展的高质量代码架构。

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

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

相关文章

terraform入门

一、概念 1、Terraform 的“基础设施即代码”是什么 基础设施即代码 (IaC) 工具允许您使用配置文件而非图形用户界面来管理基础设施。通过定义可版本控制、可重用和可共享的资源配置&#xff0c;IaC 允许您以安全、一致且可重复的方式构建、更改和管理您的基础设施。 Terraform…

ARM 体系结构与存储器

一、RAM 分类SRAM (Static RAM)用 触发器/晶体管 存储 0/1。特点&#xff1a;速度快、功耗低&#xff08;静态保持&#xff09;&#xff0c;但成本高、容量小。应用&#xff1a;片上缓存、寄存器文件、单片机内存。DRAM (Dynamic RAM)用 电容充放电 存储 0/1。特点&#xff1a;…

Jenkins运维之路(初识流水线)

1.初次使用流水线前面我们用自由风格的流水线进行了项目部署&#xff0c;但是自由风格的流水线只能应付一些简单且项目规模不是很大的部署。为了让流水线能够灵活、通用、逻辑清晰且更加容易维护&#xff0c;现在一般企业都是采取使用了Pipeline的方式来对流水线进行构建&#…

【智能协同云图库】基于统一接口架构构建多维度分析功能、结合 ECharts 可视化与权限校验实现用户 / 管理员图库统计、通过 SQL 优化与流式处理提升数据

摘要&#xff1a;本节围绕提升空间图库管理分析能力&#xff0c;先分用户与管理员两类梳理资源使用、分类、标签等 6 大分析需求&#xff0c;再设计统一实现流程与接口方案&#xff0c;最后通过分层开发完成各需求后端功能&#xff0c;覆盖权限校验、数据处理与接口编写。 本节…

HTML第八课:HTML4和HTML5的区别

HTML第八课&#xff1a;HTML4和HTML5的区别html4 与 html 5的区别快速学习平台html4 与 html 5的区别 示例图 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> &…

CS336——1. Overview

文章目录1. CS336介绍2. 概览2.1 为什么会有这门课程2.1.1 LLM的参数和训练成本2.2.2 小语言模型和LLM的区别2.2 你可以学到什么&#xff1f;2.2.1 Intuitions2.2.2 The bitter lesson3. 全景图(current landscape)/发展历史4. 可执行的课件说明5. 课程设计6. 课程内容6. 1 bas…

ncnn-Android-mediapipe_hand 踩坑部署实录

目录 同时有 nanodet 和yolox,可以做到10fps 没测试:yolox hand ncnn-Android-mediapipe_hand hand 关键点21个模型: ncnn版本: 跑通后,手部关键点集中在图像左上角,经过排查,原因是ncnn版本不对。 CMakeLists.txt 同时有 nanodet 和yolox,可以做到10fps 无训练…

OSPF实验:外部路由引入

OSPF外部路由引入实验目的&#xff1a;除了内部通信外&#xff0c;企业还需要与外部网络进行通信&#xff0c;不同企业网络之间存在互访需求。假设A公司网络部署OSPF协议实现内部通信&#xff0c;因业务发展&#xff0c;需要访问B公司的一台WEB服务器。网络拓扑图&#xff1a;公…

网络上那些在线 PDF 转换工具安全吗?转换 PDF 需要注意什么

PDF 格式凭借跨设备兼容、格式稳定的优势&#xff0c;早已成为个人办公、企业协作中不可或缺的文件格式。无论是压缩 PDF 以满足邮件附件大小限制&#xff0c;还是将 Word 文档转成 PDF 确保排版不变&#xff0c;我们总能遇到需要 PDF 工具的场景。也正因如此&#xff0c;网上涌…

生成对抗网络(GAN)

目录 1 引言 2 生成对抗网络的基本原理 2.1 生成器与判别器 2.2 对抗训练过程 2.3 与传统生成模型的比较 3 GAN的衍生模型 3.1 架构创新与深度卷积GAN 3.2 损失函数优化与Wasserstein GAN 3.3 条件生成与可控合成 3.4 跨域转换与CycleGAN 3.5 高分辨率生成与规模化演…

Vue 3.6 Alien Signals:让响应式性能飞跃式提升

概述 Vue 3.6 引入了革命性的 Alien Signals 技术&#xff0c;这是一种全新的响应式系统&#xff0c;基于细粒度响应式原理&#xff0c;为 Vue 应用带来了前所未有的性能提升和开发体验优化。 什么是 Alien Signals&#xff1f; Alien Signals 是 Vue 3.6 内置的轻量级响应式…

React Hooks 报错?一招解决useState问题

文章目录问题分析问题 在使用import { useState } from "react";时报错&#xff1a;Youre importing a component that needs useState. This React Hook only works in a Client Component. To fix, mark the file (or its parent) with the “use client” direct…

数据集成平台怎么选?从ETL到CDC再到iPaaS的全景对比

前言&#xff1a;一个制造企业的真实困境 近期在为某家制造企业做系统改造时&#xff0c;我们遇到了一个典型的数据集成难题。这家企业运营着独立的ERP、CRM和MES等30业务系统&#xff0c;看似完备的信息化基础却存在严重的数据割裂问题。 销售团队在CRM中查看的库存数据总是滞…

驱动开发系列72 - GLSL编译器实现 - 指令选择(二)

前面介绍过,在指令选择时会执行一系列优化过程,本节介绍下“比特级常量传播优化”的实现。 一:什么是比特级常量传播优化 举一个GLSL语言例子: #version 450layout(location = 0) in vec4 inColor; layout(location = 0) out vec4 outColor;void main() {vec4 tmp = inCo…

Redis(缓存)

一 什么是缓存1. 生活上的例子比如有一个行李箱和一个手机&#xff0c;每次把手机放到行李箱在拿出来肯定很麻烦&#xff0c;如果放到裤兜里就会方便很多&#xff0c;所以裤兜算作行李箱的一个缓存&#xff0c;不仅仅是裤兜&#xff0c;甚至可以一直拿在手上等其他有存储介质的…

openssl简介

一、openssl是什么 OpenSSL是一个开源的、功能强大的软件库和工具包,它实现了传输层安全(TLS) 和安全套接层(SSL) 协议以及一个全面的密码学原语库。它是用 C 语言编写的,为其带来了高性能和跨平台的特性。 作为库(Library):开发者可以将其代码集成到自己的应用程序(…

左值引用与右值引用

左值和右值 左值&#xff08;lvalue&#xff09;&#xff1a;在表达式结束后仍然存在&#xff0c;可以取地址。简单理解&#xff1a;有名字、有存储位置。 比如变量、数组元素、对象等。 右值&#xff08;rvalue&#xff09;&#xff1a;临时值&#xff0c;表达式结束后就消失&…

中小企业SAP B1 HANA部署全解析:成本与云端优势

目录 云端部署成本构成与效益分析 软件许可费 硬件成本 服务费 培训费 技术优势 快速部署 弹性扩展 高可用性 云端部署适用场景 IT预算有限的中小企业 分布在不同地区的机构 需要快速上线的情况 本地部署适用场景 数据监管严格的行业 拥有完善IT基础设施企业 …

Django Channels实战:WebSocket实时通信开发

在当今Web应用开发中&#xff0c;实时通信功能已成为提升用户体验的关键要素。传统的HTTP请求-响应模式难以满足即时聊天、实时通知、协同编辑等场景的需求。本文将深入探讨如何利用Django Channels框架实现WebSocket通信&#xff0c;为你的Django项目添加实时交互能力。为什么…