深入理解JavaScript系列(43):设计模式之状态模式详解


Posted in Javascript onMarch 04, 2015

介绍

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

正文

举个例子,就比如我们平时在下载东西,通常就会有好几个状态,比如准备状态(ReadyState)、下载状态(DownloadingState)、暂停状态(DownloadPausedState)、下载完毕状态(DownloadedState)、失败状态(DownloadFailedState),也就是说在每个状态都只可以做当前状态才可以做的事情,而不能做其它状态能做的事儿。

由于State模式描述了下载(Download)如何在每一种状态下表现出不同的行为。这一模式的关键思想就是引入了一个叫做State的抽象类(或JS里的函数)来表示下载状态,State函数(作为原型)为每个状态的子类(继承函数)声明了一些公共接口。其每个继承函数实现与特定状态相关的行为,比如DownloadingState和DownloadedState分别实现了正在下载和下载完毕的行为。这些行为可以通过Download来来维护。

让我们来实现一把,首先定义作为其他基础函数的原型的State函数:

var State = function () {
};
State.prototype.download = function () {

    throw new Error("该方法必须被重载!");

};
State.prototype.pause = function () {

    throw new Error("该方法必须被重载!");

};
State.prototype.fail = function () {

    throw new Error("该方法必须被重载!");

};
State.prototype.finish = function () {

    throw new Error("该方法必须被重载!");

};

我们为State的原型定义了4个方法接口,分别对应着下载(download)、暂停(pause)、失败(fail)、结束(finish)以便子函数可以重写。

在编写子函数之前,我们先来编写一个ReadyState函数,以便可以将状态传递给第一个download状态:

var ReadyState = function (oDownload) {

    State.apply(this);

    this.oDownload = oDownload;

};
ReadyState.prototype = new State();
ReadyState.prototype.download = function () {

    this.oDownload.setState(this.oDownload.getDownloadingState());

    // Ready以后,可以开始下载,所以设置了Download函数里的状态获取方法

 console.log("Start Download!");

};
ReadyState.prototype.pause = function () {

    throw new Error("还没开始下载,不能暂停!");

};
ReadyState.prototype.fail = function () {

    throw new Error("文件还没开始下载,怎么能说失败呢!");

};
ReadyState.prototype.finish = function () {

    throw new Error("文件还没开始下载,当然也不能结束了!");

};

该函数接收了一个Download维护函数的实例作为参数,Download函数用于控制状态的改变和获取(类似于中央控制器,让外部调用),ReadyState重写了原型的download方法,以便开始进行下载。我们继续来看Download函数的主要功能:

var Download = function () {

    this.oState = new ReadyState(this);

};
Download.prototype.setState = function (oState) {

    this.oState = oState;

};
// 对外暴露的四个公共方法,以便外部调用
Download.prototype.download = function () {

    this.oState.download();

};
Download.prototype.pause = function () {

    this.oState.pause();

};
Download.prototype.fail = function () {

    this.oState.fail();

};
Download.prototype.finish = function () {

    this.oState.finish();

};
//获取各种状态,传入当前this对象

Download.prototype.getReadyState = function () {

    return new ReadyState(this);

};
Download.prototype.getDownloadingState = function () {

    return new DownloadingState(this);

};
Download.prototype.getDownloadPausedState = function () {

    return new DownloadPausedState(this);

};
Download.prototype.getDownloadedState = function () {

    return new DownloadedState(this);

};
Download.prototype.getDownloadedFailedState = function () {

    return new DownloadFailedState(this);

};

Download函数的原型提供了8个方法,4个是对用于下载状态的操作行为,另外4个是用于获取当前四个不同的状态,这4个方法都接收this作为参数,也就是将Download实例自身作为一个参数传递给处理该请求的状态对象(ReadyState 以及后面要实现的继承函数),这使得状态对象比必要的时候可以访问oDownlaod。

接下来,继续定义4个相关状态的函数:

var DownloadingState = function (oDownload) {

    State.apply(this);

    this.oDownload = oDownload;

};
DownloadingState.prototype = new State();
DownloadingState.prototype.download = function () {

    throw new Error("文件已经正在下载中了!");

};
DownloadingState.prototype.pause = function () { this.oDownload.setState(this.oDownload.getDownloadPausedState());

    console.log("暂停下载!");

};
DownloadingState.prototype.fail = function () { this.oDownload.setState(this.oDownload.getDownloadedFailedState());

    console.log("下载失败!");

};
DownloadingState.prototype.finish = function () {

    this.oDownload.setState(this.oDownload.getDownloadedState());

    console.log("下载完毕!");

};

DownloadingState的主要注意事项就是已经正在下载的文件,不能再次开始下载了,其它的状态都可以连续进行。

var DownloadPausedState = function (oDownload) {

    State.apply(this);

    this.oDownload = oDownload;

};
DownloadPausedState.prototype = new State();
DownloadPausedState.prototype.download = function () {

    this.oDownload.setState(this.oDownload.getDownloadingState());

    console.log("继续下载!");

};
DownloadPausedState.prototype.pause = function () {

    throw new Error("已经暂停了,咋还要暂停呢!");

};
DownloadPausedState.prototype.fail = function () { this.oDownload.setState(this.oDownload.getDownloadedFailedState());

    console.log("下载失败!");

};
DownloadPausedState.prototype.finish = function () {

    this.oDownload.setState(this.oDownload.getDownloadedState());

    console.log("下载完毕!");

};

DownloadPausedState函数里要注意的是,已经暂停的下载,不能再次暂停。
var DownloadedState = function (oDownload) {

    State.apply(this);

    this.oDownload = oDownload;

};
DownloadedState.prototype = new State();
DownloadedState.prototype.download = function () {

    this.oDownload.setState(this.oDownload.getDownloadingState());

    console.log("重新下载!");

};
DownloadedState.prototype.pause = function () {

    throw new Error("对下载完了,还暂停啥?");

};
DownloadedState.prototype.fail = function () {

    throw new Error("都下载成功了,咋会失败呢?");

};
DownloadedState.prototype.finish = function () {

    throw new Error("下载成功了,不能再为成功了吧!");

};

DownloadedState函数,同理成功下载以后,不能再设置finish了,只能设置重新下载状态。

var DownloadFailedState = function (oDownload) {

    State.apply(this);

    this.oDownload = oDownload;

};
DownloadFailedState.prototype = new State();
DownloadFailedState.prototype.download = function () {

    this.oDownload.setState(this.oDownload.getDownloadingState());

    console.log("尝试重新下载!");

};
DownloadFailedState.prototype.pause = function () {

    throw new Error("失败的下载,也不能暂停!");

};
DownloadFailedState.prototype.fail = function () {

    throw new Error("都失败了,咋还失败呢!");

};
DownloadFailedState.prototype.finish = function () {

    throw new Error("失败的下载,肯定也不会成功!");

};

同理,DownloadFailedState函数的失败状态,也不能再次失败,但可以和finished以后再次尝试重新下载。

调用测试代码,就非常简单了,我们在HTML里演示吧,首先是要了jquery,然后有3个按钮分别代表:开始下载、暂停、重新下载。(注意在Firefox里用firebug查看结果,因为用了 console.log方法)。

<html>

<head>

    <link type="text/css" rel="stylesheet" href="http://www.cnblogs.com/css/style.css" />

    <title>State Pattern</title>

    <script type="text/javascript" src="/jquery.js"></script>

    <script type="text/javascript" src="Download.js"></script>

    <script type="text/javascript" src="states/State.js"></script>

    <script type="text/javascript" src="states/DownloadFailedState.js"></script>

    <script type="text/javascript" src="states/DownloadPausedState.js"></script>

    <script type="text/javascript" src="states/DownloadedState.js"></script>

    <script type="text/javascript" src="states/DownloadingState.js"></script>

    <script type="text/javascript" src="states/ReadyState.js"></script>

</head>

<body>

    <input type="button" value="开始下载" id="download_button" />

    <input type="button" value="暂停" id="pause_button" />

    <input type="button" value="重新下载" id="resume_button" />

    <script type="text/javascript">

        var oDownload = new Download();

        $("#download_button").click(function () {

            oDownload.download();

        });
        $("#pause_button").click(function () {

            oDownload.pause();

        });
        $("#resume_button").click(function () {

            oDownload.download();

        });

    </script>

</body>

</html>

总结

状态模式的使用场景也特别明确,有如下两点:

1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2.一个操作中含有大量的分支语句,而且这些分支语句依赖于该对象的状态。状态通常为一个或多个枚举常量的表示。

Javascript 相关文章推荐
jscript之List Excel Color Values
Jun 13 Javascript
javascript URL编码和解码使用说明
Apr 12 Javascript
JavaScript学习点滴 call、apply的区别
Oct 22 Javascript
Ext GridPanel加载完数据后进行操作示例代码
Jun 17 Javascript
优化Node.js Web应用运行速度的10个技巧
Sep 03 Javascript
JavaScript实现点击文本自动定位到下拉框选中操作
Jun 15 Javascript
jQuery插件HighCharts绘制简单2D折线图效果示例【附demo源码】
Mar 21 jQuery
讲解vue-router之命名路由和命名视图
May 28 Javascript
详解写好JS条件语句的5条守则
Feb 28 Javascript
vue.js使用v-model实现表单元素(input) 双向数据绑定功能示例
Mar 08 Javascript
Javascript数组方法reduce的妙用之处分享
Jun 10 Javascript
基于javascript实现移动端轮播图效果
Dec 21 Javascript
深入理解JavaScript系列(42):设计模式之原型模式详解
Mar 04 #Javascript
javascript 动态创建表格的2种方法总结
Mar 04 #Javascript
深入理解JavaScript系列(41):设计模式之模板方法详解
Mar 04 #Javascript
深入理解JavaScript系列(40):设计模式之组合模式详解
Mar 04 #Javascript
百度地图自定义控件分享
Mar 04 #Javascript
jQuery实现仿淘宝带有指示条的图片转动切换效果完整实例
Mar 04 #Javascript
深入理解JavaScript系列(39):设计模式之适配器模式详解
Mar 04 #Javascript
You might like
简单实用的.net DataTable导出Execl
2013/10/28 PHP
php实现数组筛选奇数和偶数示例
2014/04/11 PHP
JS实现div居中示例
2014/04/17 Javascript
jQuery的事件委托实例分析
2015/07/15 Javascript
详解JavaScript添加给定的标签选项
2018/09/17 Javascript
在微信小程序中渲染HTML内容的方法示例
2018/09/28 Javascript
vue 配置多页面应用的示例代码
2018/10/22 Javascript
浅探express路由和中间件的实现
2019/09/30 Javascript
[01:03:36]Ti4 循环赛第三日DK vs Titan
2014/07/12 DOTA
python中使用百度音乐搜索的api下载指定歌曲的lrc歌词
2014/07/18 Python
使用Python实现下载网易云音乐的高清MV
2015/03/16 Python
python计算文本文件行数的方法
2015/07/06 Python
Django中对数据查询结果进行排序的方法
2015/07/17 Python
结合Python的SimpleHTTPServer源码来解析socket通信
2016/06/27 Python
python去掉行尾的换行符方法
2017/01/04 Python
Python使用正则表达式实现文本替换的方法
2017/04/18 Python
Python2.7基于笛卡尔积算法实现N个数组的排列组合运算示例
2017/11/23 Python
python3+mysql查询数据并通过邮件群发excel附件
2018/02/24 Python
python统计多维数组的行数和列数实例
2018/06/23 Python
django celery redis使用具体实践
2019/04/08 Python
python反编译学习之字节码详解
2019/05/19 Python
Django框架中间件定义与使用方法案例分析
2019/11/28 Python
PyQt5中多线程模块QThread使用方法的实现
2020/01/31 Python
python属于解释型语言么
2020/06/15 Python
html5移动端价格输入键盘的实现
2019/09/16 HTML / CSS
Skyscanner阿联酋:全球领先的旅游搜索平台
2017/11/25 全球购物
老同学聚会感言
2014/02/23 职场文书
推荐信怎么写
2014/05/09 职场文书
村党支部对照检查材料思想汇报
2014/09/28 职场文书
2014年团支书工作总结
2014/11/14 职场文书
导游词300字
2015/02/13 职场文书
副总经理岗位职责范本
2015/04/08 职场文书
立项申请报告范本
2015/05/15 职场文书
2015国庆66周年宣传语
2015/07/14 职场文书
Node与Python 双向通信的实现代码
2021/07/16 Javascript
python实现商品进销存管理系统
2022/05/30 Python