C语言实现状态模式

状态模式(State Pattern)的核心是允许对象在内部状态改变时改变其行为 ,使对象看起来好像修改了它的类。在C语言中,可以通过状态结构体(封装不同状态的行为)+ 上下文结构体(持有当前状态并委托行为) 实现:上下文将行为委托给当前状态对象,状态变化时只需切换上下文持有的状态指针。

C语言实现状态模式的思路

  1. 状态接口(State) :定义所有具体状态的统一行为接口(函数指针),如handle(处理事件)。
  2. 具体状态(Concrete State):实现状态接口,封装对应状态下的具体行为,并在适当时候触发状态转换。
  3. 上下文(Context):持有当前状态的指针,提供统一接口供客户端调用,将具体行为委托给当前状态,同时允许状态切换。

示例:电梯状态管理(运行、停止、开门、关门)

电梯有多种状态(停止、运行、开门、关门),不同状态下对同一事件(如按开门键)的响应不同。用状态模式管理电梯状态转换和行为。

步骤1:定义状态接口和上下文
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 前向声明上下文,避免循环依赖
typedef struct Elevator Elevator;

// 状态接口:定义电梯在不同状态下的行为
typedef struct State {
    // 处理开门事件
    void (*open)(struct State* self, Elevator* elevator);
    // 处理关门事件
    void (*close)(struct State* self, Elevator* elevator);
    // 处理运行事件
    void (*run)(struct State* self, Elevator* elevator);
    // 处理停止事件
    void (*stop)(struct State* self, Elevator* elevator);
    // 状态名称(用于打印)
    char name[20];
} State;
步骤2:实现上下文(电梯)

上下文持有当前状态,提供触发事件的接口,并允许状态切换。

c 复制代码
// 上下文:电梯
typedef struct Elevator {
    State* current_state; // 当前状态
    int floor;            // 当前楼层
} Elevator;

// 电梯初始化
void elevator_init(Elevator* elevator, State* initial_state) {
    elevator->current_state = initial_state;
    elevator->floor = 1; // 初始在1楼
    printf("电梯初始化,初始状态:%s(%d楼)\n", initial_state->name, elevator->floor);
}

// 触发开门事件(委托给当前状态)
void elevator_open(Elevator* elevator) {
    printf("\n触发开门...\n");
    elevator->current_state->open(elevator->current_state, elevator);
}

// 触发关门事件
void elevator_close(Elevator* elevator) {
    printf("\n触发关门...\n");
    elevator->current_state->close(elevator->current_state, elevator);
}

// 触发运行事件
void elevator_run(Elevator* elevator) {
    printf("\n触发运行...\n");
    elevator->current_state->run(elevator->current_state, elevator);
}

// 触发停止事件
void elevator_stop(Elevator* elevator) {
    printf("\n触发停止...\n");
    elevator->current_state->stop(elevator->current_state, elevator);
}

// 切换电梯状态(由具体状态调用)
void elevator_change_state(Elevator* elevator, State* new_state) {
    elevator->current_state = new_state;
    printf("状态切换为:%s\n", new_state->name);
}
步骤3:实现具体状态(停止、运行、开门、关门)

每个状态实现State接口,定义该状态下对事件的响应,并在满足条件时切换到其他状态。

3.1 停止状态(电梯静止在某楼层)
c 复制代码
// 前向声明其他状态,用于状态转换
extern State stopping_state;
extern State running_state;
extern State opening_state;
extern State closing_state;

// 具体状态1:停止状态
static void stop_open(State* self, Elevator* elevator) {
    // 停止状态下可以开门
    printf("电梯在%d楼停止状态,执行开门...", elevator->floor);
    elevator_change_state(elevator, &opening_state); // 切换到开门状态
}

static void stop_close(State* self, Elevator* elevator) {
    // 停止状态下关门无意义
    printf("电梯已停止且门关闭,无需关门\n");
}

static void stop_run(State* self, Elevator* elevator) {
    // 停止状态下可以运行
    printf("电梯在%d楼停止状态,开始运行...", elevator->floor);
    elevator->floor = 5; // 模拟运行到5楼
    elevator_change_state(elevator, &running_state); // 切换到运行状态
}

static void stop_stop(State* self, Elevator* elevator) {
    // 已在停止状态
    printf("电梯已处于停止状态\n");
}

// 初始化停止状态
State stopping_state = {
    .open = stop_open,
    .close = stop_close,
    .run = stop_run,
    .stop = stop_stop,
    .name = "停止"
};
3.2 运行状态(电梯正在移动)
c 复制代码
// 具体状态2:运行状态
static void run_open(State* self, Elevator* elevator) {
    // 运行中不能开门
    printf("电梯运行中,禁止开门\n");
}

static void run_close(State* self, Elevator* elevator) {
    // 运行中门已关闭,无需关门
    printf("电梯运行中,门已关闭\n");
}

static void run_run(State* self, Elevator* elevator) {
    // 已在运行状态
    printf("电梯已处于运行状态\n");
}

static void run_stop(State* self, Elevator* elevator) {
    // 运行状态下可以停止
    printf("电梯从%d楼停止运行...", elevator->floor);
    elevator_change_state(elevator, &stopping_state); // 切换到停止状态
}

// 初始化运行状态
State running_state = {
    .open = run_open,
    .close = run_close,
    .run = run_run,
    .stop = run_stop,
    .name = "运行"
};
3.3 开门状态(电梯门打开)
c 复制代码
// 具体状态3:开门状态
static void open_open(State* self, Elevator* elevator) {
    // 已在开门状态
    printf("电梯门已打开\n");
}

static void open_close(State* self, Elevator* elevator) {
    // 开门状态下可以关门
    printf("电梯门开始关闭...");
    elevator_change_state(elevator, &closing_state); // 切换到关门状态
}

static void open_run(State* self, Elevator* elevator) {
    // 门开着不能运行
    printf("电梯门未关闭,无法运行\n");
}

static void open_stop(State* self, Elevator* elevator) {
    // 开门状态下已停止
    printf("电梯门打开,已处于停止状态\n");
}

// 初始化开门状态
State opening_state = {
    .open = open_open,
    .close = open_close,
    .run = open_run,
    .stop = open_stop,
    .name = "开门"
};
3.4 关门状态(电梯门关闭中)
c 复制代码
// 具体状态4:关门状态
static void close_open(State* self, Elevator* elevator) {
    // 关门中可以重新开门
    printf("电梯门停止关闭,开始打开...");
    elevator_change_state(elevator, &opening_state); // 切换到开门状态
}

static void close_close(State* self, Elevator* elevator) {
    // 已在关门状态
    printf("电梯门已关闭\n");
}

static void close_run(State* self, Elevator* elevator) {
    // 门关闭后可以运行
    printf("电梯门关闭完成,开始运行...");
    elevator->floor = 3; // 模拟运行到3楼
    elevator_change_state(elevator, &running_state); // 切换到运行状态
}

static void close_stop(State* self, Elevator* elevator) {
    // 关门后进入停止状态
    printf("电梯门关闭完成,停止不动...");
    elevator_change_state(elevator, &stopping_state); // 切换到停止状态
}

// 初始化关门状态
State closing_state = {
    .open = close_open,
    .close = close_close,
    .run = close_run,
    .stop = close_stop,
    .name = "关门"
};
步骤4:使用状态模式

客户端通过上下文(电梯)触发事件,无需关心具体状态,状态转换由状态对象内部处理。

c 复制代码
int main() {
    // 创建电梯(初始状态为停止)
    Elevator elevator;
    elevator_init(&elevator, &stopping_state);
    
    // 模拟电梯操作流程
    elevator_open(&elevator);    // 停止→开门
    elevator_close(&elevator);   // 开门→关门
    elevator_run(&elevator);     // 关门→运行
    elevator_stop(&elevator);    // 运行→停止
    elevator_run(&elevator);     // 停止→运行
    elevator_open(&elevator);    // 运行中开门(禁止)
    elevator_stop(&elevator);    // 运行→停止
    
    return 0;
}

输出结果

复制代码
电梯初始化,初始状态:停止(1楼)

触发开门...
电梯在1楼停止状态,执行开门...状态切换为:开门

触发关门...
电梯门开始关闭...状态切换为:关门

触发运行...
电梯门关闭完成,开始运行...状态切换为:运行

触发停止...
电梯从3楼停止运行...状态切换为:停止

触发运行...
电梯在3楼停止状态,开始运行...状态切换为:运行

触发开门...
电梯运行中,禁止开门

触发停止...
电梯从5楼停止运行...状态切换为:停止

核心思想总结

  1. 行为与状态绑定 :每个状态封装了该状态下的行为(如stopping_stateopen方法允许开门),避免了用if-elseswitch判断状态的硬编码。
  2. 状态转换清晰 :状态转换逻辑由具体状态对象控制(如closing_staterun方法切换到running_state),职责明确。
  3. 扩展性好 :新增状态(如"故障状态")只需实现State接口,无需修改上下文或其他状态代码,符合开放-封闭原则
  4. 上下文与状态解耦:上下文(电梯)只需委托行为给当前状态,无需知道具体状态的实现,降低耦合。

C语言通过结构体封装状态行为和状态转换逻辑,结合上下文委托机制,完美实现了状态模式的核心。这种模式适合处理对象行为随状态变化而变化的场景(如订单状态、设备状态、游戏角色状态等)。

相关推荐
q***64971 小时前
VS与SQL Sever(C语言操作数据库)
c语言·数据库·sql
chinesegf7 小时前
图文并茂的笔记、便签是如何用py开发的
笔记·状态模式
口袋物联9 小时前
设计模式之工厂模式在 C 语言中的应用(含 Linux 内核实例)
linux·c语言·设计模式·简单工厂模式
Want59511 小时前
C/C++跳动的爱心①
c语言·开发语言·c++
lingggggaaaa11 小时前
免杀对抗——C2远控篇&C&C++&DLL注入&过内存核晶&镂空新增&白加黑链&签名程序劫持
c语言·c++·学习·安全·网络安全·免杀对抗
gfdhy11 小时前
【c++】哈希算法深度解析:实现、核心作用与工业级应用
c语言·开发语言·c++·算法·密码学·哈希算法·哈希
我不会插花弄玉12 小时前
vs2022调试基础篇【由浅入深-C语言】
c语言
福尔摩斯张13 小时前
《C 语言指针从入门到精通:全面笔记 + 实战习题深度解析》(超详细)
linux·运维·服务器·c语言·开发语言·c++·算法
fashion 道格13 小时前
数据结构实战:深入理解队列的链式结构与实现
c语言·数据结构
铁手飞鹰14 小时前
二叉树(C语言,手撕)
c语言·数据结构·算法·二叉树·深度优先·广度优先