設計模式(策略,觀察者,單例,工廠方法)

文章目錄

    • 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
英文地址,請注明出處:http://en.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項目添加實時交互能力。為什么…