概述
状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
之前的文章已经详细阐述了这种设计模式的核心和注意事项,并完成了基类设计,请参见《C语言 - 状态模式(基类部分)》,本文将结合实际案例论证这种设计模式,加深读者对状态模式的理解和应用。
示例
★背景说明:音乐播放器包含四种状态,播放态、暂停态、快进态、停止态,而四种状态根据外界按键是否按下决定,按键包含三种(play stop speed),当按下按键,对应的状态将会自行改变。
★环境对象(music):
属性:无
行为:按下播放键、按下快进键、按下停止键、获取各按键状态、处理函数。
继承:继承环境类Context基类
★状态对象(播放态、暂停态、快进态、停止态,每种状态单例设计):
属性:无
行为:无。
继承:继承状态类State基类
★包含头文件music.h和源文件music.c(均已验证通过)
music.h
/** * @Filename : music.h * @Revision : $Revision: 1.0 $ * @Author : Feng(微信公众号:不只会拍照的程序猿) * @Description : 状态模式应用(C语言模拟C++) * @Explain : 音乐播放包含四种状态: 播放态 非播放态下play=1 play 单例模式 快进态 播放态下speed=1 speed 单例模式 停止态 播放态和快进态下play=1 stop 单例模式 暂停态 播放态和快进态下pause=1 pause 单例模式 **/ #ifndef __music_H__ #define __music_H__ #include <stdio.h> #include <string.h> #include <stdlib.h> #include "state.h" /* 按键定义 */ struct t_button { int play; /* 播放键 */ int speed; /* 快进键 */ int stop; /* 停止键 */ }; /* 音乐播放器类定义 */ struct music { struct context context; /* 继承当前状态基类 */ void (*play)(struct music *p_music); /* 按下播放键 */ void (*speed)(struct music *p_music); /* 按下快进键 */ void (*stop)(struct music *p_music); /* 按下停止键 */ struct t_button * (*get)(struct music *p_music); /* 获取各按键状态 */ void (*handle)(struct music *p_music); /* 处理函数 */ }; /** * @创建music对象 * @成功返回类对象,失败返回NULL **/ struct music *new_music(void); /* 播放状态类定义 */ struct play { struct state state; }; /* 快进状态类定义 */ struct speed { struct state state; }; /* 停止状态类定义 */ struct stop { struct state state; }; /* 暂停状态类定义 */ struct pause { struct state state; }; /** * @创建播放对象 * @name:类名 * @成功返回类对象,失败返回NULL **/ struct play *new_play(char *name); /** * @创建快进状态对象 * @name:类名 * @成功返回类对象,失败返回NULL **/ struct speed *new_speed(char *name); /** * @创建停止状态对象 * @name:类名 * @成功返回类对象,失败返回NULL **/ struct stop *new_stop(char *name); /** * @创建暂停状态对象 * @name:类名 * @成功返回类对象,失败返回NULL **/ struct pause *new_pause(char *name); #endif
music.c
/** * @Filename : music.c * @Revision : $Revision: 1.0 $ * @Author : Feng(微信公众号:不只会拍照的程序猿) * @Description : 状态模式应用(C语言模拟C++) * @Explain : 音乐播放包含四种状态: 播放态 非播放态下play=1 play 单例模式 快进态 播放态下speed=1 speed 单例模式 停止态 播放态和快进态下play=1 stop 单例模式 暂停态 播放态和快进态下pause=1 pause 单例模式 **/ #include "music.h" /** * @获取按键地址 * @p_music:music类 * @返回按键地址 **/ static struct t_button *_get(struct music *p_music) { struct context *p_context = (struct context *)p_music; return ((struct t_button *)CLASS_CALL_0(p_context, get_info)); } /** * @清零信息 * @p_music:music类 **/ static struct t_button *_clear_info(struct music *p_music) { struct t_button *p_tmp = (struct t_button *)CLASS_CALL_0(p_music, get); if (p_tmp == NULL) p_tmp = (struct t_button *)malloc(sizeof(struct t_button)); memset(p_tmp, 0, sizeof(struct t_button)); } /** * @按下播放键 * @p_music:music类 **/ static void _play(struct music *p_music) { struct context *p_context = (struct context *)p_music; struct t_button *p_tmp = _clear_info(p_music); p_tmp->play = 1; CLASS_CALL(p_context, set_info, (void *)p_tmp); } /** * @按下快进键 * @p_music:music类 **/ static void _speed(struct music *p_music) { struct context *p_context = (struct context *)p_music; struct t_button *p_tmp = _clear_info(p_music); p_tmp->speed = 1; CLASS_CALL(p_context, set_info, (void *)p_tmp); } /** * @按下停止键 * @p_music:music类 **/ static void _stop(struct music *p_music) { struct context *p_context = (struct context *)p_music; struct t_button *p_tmp = _clear_info(p_music); p_tmp->stop = 1; CLASS_CALL(p_context, set_info, (void *)p_tmp); } /** * @处理函数 * @p_music:music类 **/ static void _handle(struct music *p_music) { struct state *p_state = p_music->context.get(&(p_music->context)); if (CLASS_CALL(p_music->context.p_dll, find_key, p_state) != NULL) CLASS_CALL(p_state, handle, (struct context *)p_music); } /** * @创建music对象 * @成功返回类对象,失败返回NULL **/ struct music *new_music(void) { struct context *p_context; struct music *p_music; p_music = (struct music *)malloc(sizeof(struct music)); if (p_music == NULL) return NULL; memset((char *)p_music, 0, sizeof(struct music)); if ((p_context = new_context()) == NULL) { free(p_music); return NULL; } memcpy(&(p_music->context), p_context, sizeof(struct context)); free(p_context); p_music->get = _get; p_music->play = _play; p_music->speed = _speed; p_music->stop = _stop; p_music->handle = _handle; return p_music; } /** * @播放状态匹配判断 * @p_state:状态 p_context:当前状态类 * @匹配返回0,否则返回-1 **/ static int _play_match(struct state *p_state, struct context *p_context) { struct state *p_cstate = CLASS_CALL_0(p_context, get); struct t_button *p_button = (struct t_button *)(p_context->p_info); if (((!strcmp(p_cstate->name, "stop")) || (!strcmp(p_cstate->name, "pause")) || (!strcmp(p_cstate->name, "speed"))) && (p_button->play)) return 0; return (-1); } /** * @暂停状态匹配判断 * @p_state:状态 p_context:当前状态类 * @匹配返回0,否则返回-1 **/ static int _pause_match(struct state *p_state, struct context *p_context) { struct state *p_cstate = CLASS_CALL_0(p_context, get); struct t_button *p_button = (struct t_button *)(p_context->p_info); if (((!strcmp(p_cstate->name, "play")) || (!strcmp(p_cstate->name, "speed"))) && (p_button->play)) return 0; return (-1); } /** * @停止状态匹配判断 * @p_state:状态 p_context:当前状态类 * @匹配返回0,否则返回-1 **/ static int _stop_match(struct state *p_state, struct context *p_context) { struct state *p_cstate = CLASS_CALL_0(p_context, get); struct t_button *p_button = (struct t_button *)(p_context->p_info); if (((!strcmp(p_cstate->name, "play")) || (!strcmp(p_cstate->name, "speed"))) && (p_button->stop)) return 0; return (-1); } /** * @快进状态匹配判断 * @p_state:状态 p_context:当前状态类 * @匹配返回0,否则返回-1 **/ static int _speed_match(struct state *p_state, struct context *p_context) { struct state *p_cstate = CLASS_CALL_0(p_context, get); struct t_button *p_button = (struct t_button *)(p_context->p_info); if ((!strcmp(p_cstate->name, "play")) && (p_button->speed)) return 0; return (-1); } /** * @不同状态下的执行函数,实际应用中可分开写 * @p_state:状态 p_context:当前状态类 **/ static void _state_handle(struct state *p_state, struct context *p_context) { printf("my state is %s\n", p_state->name); } /** * @获取状态名 * @p_state:状态 * @返回当前状态名 **/ static char *_get_name(struct state *p_state) { return (p_state->name); } /** * @创建播放对象 * @name:类名 * @成功返回类对象,失败返回NULL **/ struct play *new_play(char *name) { struct state *p_state; static struct play *p_play = NULL; if (p_play != NULL) return p_play; p_play = (struct play *)malloc(sizeof(struct play)); if (p_play == NULL) return NULL; memset((char *)p_play, 0, sizeof(struct play)); if ((p_state = (struct state *)malloc(sizeof(struct state))) == NULL) { free(p_play); return NULL; } strcpy(p_state->name, name); p_state->get_name = _get_name; p_state->match = _play_match; p_state->handle = _state_handle; memcpy(&(p_play->state), p_state, sizeof(struct state)); free(p_state); return p_play; } /** * @创建快进状态对象 * @name:类名 * @成功返回类对象,失败返回NULL **/ struct speed *new_speed(char *name) { struct state *p_state; static struct speed *p_speed = NULL; if (p_speed != NULL) return p_speed; p_speed = (struct speed *)malloc(sizeof(struct speed)); if (p_speed == NULL) return NULL; memset((char *)p_speed, 0, sizeof(struct speed)); if ((p_state = (struct state *)malloc(sizeof(struct state))) == NULL) { free(p_speed); return NULL; } strcpy(p_state->name, name); p_state->get_name = _get_name; p_state->match = _speed_match; p_state->handle = _state_handle; memcpy(&(p_speed->state), p_state, sizeof(struct state)); free(p_state); return p_speed; } /** * @创建停止状态对象 * @name:类名 * @成功返回类对象,失败返回NULL **/ struct stop *new_stop(char *name) { struct state *p_state; static struct stop *p_stop = NULL; if (p_stop != NULL) return p_stop; p_stop = (struct stop *)malloc(sizeof(struct stop)); if (p_stop == NULL) return NULL; memset((char *)p_stop, 0, sizeof(struct stop)); if ((p_state = (struct state *)malloc(sizeof(struct state))) == NULL) { free(p_stop); return NULL; } strcpy(p_state->name, name); p_state->get_name = _get_name; p_state->match = _stop_match; p_state->handle = _state_handle; memcpy(&(p_stop->state), p_state, sizeof(struct state)); free(p_state); return p_stop; } /** * @创建暂停状态对象 * @name:类名 * @成功返回类对象,失败返回NULL **/ struct pause *new_pause(char *name) { struct state *p_state; static struct pause *p_pause = NULL; if (p_pause != NULL) return p_pause; p_pause = (struct pause *)malloc(sizeof(struct pause)); if (p_pause == NULL) return NULL; memset((char *)p_pause, 0, sizeof(struct pause)); if ((p_state = (struct state *)malloc(sizeof(struct state))) == NULL) { free(p_pause); return NULL; } strcpy(p_state->name, name); p_state->get_name = _get_name; p_state->match = _pause_match; p_state->handle = _state_handle; memcpy(&(p_pause->state), p_state, sizeof(struct state)); free(p_state); return p_pause; } /** * @主函数,演示代码 **/ int main(void) { struct music *p_music = new_music(); struct play *p_play = new_play("play"); struct pause *p_pause = new_pause("pause"); struct speed *p_speed = new_speed("speed"); struct stop *p_stop = new_stop("stop"); p_music->context.add((struct context *)p_music, (struct state *)p_play); p_music->context.add((struct context *)p_music, (struct state *)p_speed); p_music->context.add((struct context *)p_music, (struct state *)p_pause); p_music->context.add((struct context *)p_music, (struct state *)p_stop); /* 初始为播放状态 */ p_music->context.change((struct context *)p_music, (struct state *)p_play); CLASS_CALL_0(p_music, play); /* 按下播放键,暂停 */ printf("---------------------------------------\n"); CLASS_CALL_0(p_music, stop); /* 按下停止键,无效 */ printf("---------------------------------------\n"); CLASS_CALL_0(p_music, play); /* 按下播放键,播放 */ printf("---------------------------------------\n"); CLASS_CALL_0(p_music, stop); /* 按下停止键,停止 */ printf("---------------------------------------\n"); CLASS_CALL_0(p_music, play); /* 按下播放键,播放 */ printf("---------------------------------------\n"); CLASS_CALL_0(p_music, speed); /* 按下快进键,快进 */ return 0; }
结论
输入示例代码运行,结果如下:
feng:state$ gcc -o music music.c state.c class_dll.c dll.c feng:state$ ./music init state : play state: play change to state : pause --------------------------------------- --------------------------------------- state: pause change to state : play --------------------------------------- state: play change to state : stop --------------------------------------- state: stop change to state : play --------------------------------------- state: play change to state : speed feng:state$
分析:示例定义了music(MP3播放器)对象作为环境类,同时定义了play(播放)、pause(暂停)、stop(停止)和speed(快进)四种状态对象,状态之间通过用户按下按键进行响应的转换。
关注
更多精彩内容,请关注微信公众号:不只会拍照的程序猿,本人致力分享linux、设计模式、C语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要本文章源码请关注公众号:不只会拍照的程序猿,后台回复:设计模式源码,也可点击此处下载。