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