我用C语言玩对象,状态应用2-MP3

概述

状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

之前的文章已经详细阐述了这种设计模式的核心和注意事项,并完成了基类设计,请参见《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语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要本文章源码请关注公众号:不只会拍照的程序猿,后台回复:设计模式源码,也可点击此处下载

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不只会拍照的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值