C语言实现状态模式
状态模式(State Pattern)的核心是允许对象在内部状态改变时改变其行为,使对象看起来好像修改了它的类。在C语言中,可以通过状态结构体(封装不同状态的行为)+ 上下文结构体(持有当前状态并委托行为) 实现:上下文将行为委托给当前状态对象,状态变化时只需切换上下文持有的状态指针。
C语言实现状态模式的思路
- 状态接口(State):定义所有具体状态的统一行为接口(函数指针),如
handle(处理事件)。 - 具体状态(Concrete State):实现状态接口,封装对应状态下的具体行为,并在适当时候触发状态转换。
- 上下文(Context):持有当前状态的指针,提供统一接口供客户端调用,将具体行为委托给当前状态,同时允许状态切换。
示例:电梯状态管理(运行、停止、开门、关门)
电梯有多种状态(停止、运行、开门、关门),不同状态下对同一事件(如按开门键)的响应不同。用状态模式管理电梯状态转换和行为。
步骤1:定义状态接口和上下文
#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:实现上下文(电梯)
上下文持有当前状态,提供触发事件的接口,并允许状态切换。
// 上下文:电梯
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 停止状态(电梯静止在某楼层)
// 前向声明其他状态,用于状态转换
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 运行状态(电梯正在移动)
// 具体状态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 开门状态(电梯门打开)
// 具体状态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 关门状态(电梯门关闭中)
// 具体状态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:使用状态模式
客户端通过上下文(电梯)触发事件,无需关心具体状态,状态转换由状态对象内部处理。
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楼停止运行...状态切换为:停止
核心思想总结
- 行为与状态绑定:每个状态封装了该状态下的行为(如
stopping_state的open方法允许开门),避免了用if-else或switch判断状态的硬编码。 - 状态转换清晰:状态转换逻辑由具体状态对象控制(如
closing_state的run方法切换到running_state),职责明确。 - 扩展性好:新增状态(如“故障状态”)只需实现
State接口,无需修改上下文或其他状态代码,符合开放-封闭原则。 - 上下文与状态解耦:上下文(电梯)只需委托行为给当前状态,无需知道具体状态的实现,降低耦合。
C语言通过结构体封装状态行为和状态转换逻辑,结合上下文委托机制,完美实现了状态模式的核心。这种模式适合处理对象行为随状态变化而变化的场景(如订单状态、设备状态、游戏角色状态等)。
