一、什么是状态模式?
状态模式的定义:
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态模式的优点:
- 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
- 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
- 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
状态模式的缺点:
- 状态模式的使用必然会增加系统的类与对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
- 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
状态模式的应用场景:
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
二、文件切片上传
文件切片上传是一个比较常见的需求。其主要步骤如下:
- 读取文件基本信息
- 将文件按照指定大小切片
- 创建上传记录,用以标识文件片段
- 依次、并行上传文件片段(需注意浏览器的最大并行请求数)
另外还涉及到删除、重新上传等功能。
在文件上传的不同阶段,对文件不同操作的处理逻辑是不一样的,会用到大量if-else语句。
我们先看一段关于删除文件的代码:
Upload.delete = function() {
if (/* 创建上传记录中 */) {
/* 取消创建上传记录 */
/* 删除本条数据 */
} else if (/* 创建上传记录成功 */) {
/* 删除上传记录 */
/* 删除本条数据 */
} else if (/* 创建上传记录失败 */) {
/* 删除本条数据 */
} else if (/* 文件上传中 */) {
/* 删除上传记录 */
/* 取消上传 */
/* 删除本条数据 */
} else if (/* 文件上传失败 */) {
/* 删除上传记录 */
/* 删除本条数据 */
} else if (/* 文件上传成功 */) {
/* 删除上传记录 */
/* 删除文件 */
/* 删除本条数据 */
}
}
如果后续需求调整,比如新增/*文件暂停上传*/的场景,需要改写else-if逻辑,这会使得我们的代码变得越来越庞大,从而变得难以维护和阅读。而且,在实际开发中,需要处理的逻辑肯定比这多得多,因此,重构此类代码是非常有必要的。
三、重构以上代码
1、我们先为文件上传的每个阶段都定义一个状态类:
/* 创建上传记录中 */
let CreatingState = function () { }
/* 创建上传记录成功 */
let CreateSucceedState = function () { }
/* 创建上传记录失败 */
let CreateFailState = function () { }
/* 文件上传中 */
let UploadingState = function () { }
/* 文件上传成功 */
let UploadSucceedState = function () { }
/* 文件上传失败 */
let UploadErrorState = function () { }
2、分别为每个状态类设置删除方法
/* 创建上传记录中 */
CreatingState.prototype.delete = function () {
/* 取消创建上传记录 */
/* 删除本条数据 */
}
/* 创建上传记录成功 */
CreatingState.prototype.delete = function () {
/* 删除上传记录 */
/* 删除本条数据 */
}
/* 创建上传记录失败 */
CreatingState.prototype.delete = function () {
/* 删除本条数据 */
}
/* 文件上传中 */
CreatingState.prototype.delete = function () {
/* 删除上传记录 */
/* 取消上传 */
/* 删除本条数据 */
}
/* 文件上传成功 */
CreatingState.prototype.delete = function () {
/* 删除上传记录 */
/* 删除本条数据 */
}
/* 文件上传失败 */
CreatingState.prototype.delete = function () {
/* 删除上传记录 */
/* 删除文件 */
/* 删除本条数据 */
}
3、创建Upload构造函数,为每种状态类创建一个示例对象
let Upload = function () {
this.creatingState = new CreatingState(this);
this.createSucceedState = new CreateSucceedState(this);
this.createFailState = new CreateFailState(this);
this.uploadingState = new UploadingState(this);
this.uploadSucceedState = new UploadSucceedState(this);
this.uploadErrorState = new UploadErrorState(this);
this.deleteSucceedState = new DeleteSucceedState(this)
}
4、在不同的上传阶段变更Upload的状态,也是为了将事件委托给对应的状态类来执行
/* 创建上传记录中 */
this.uploadState = this.creatingState
/* 创建上传记录成功 */
this.uploadState = this.createSucceedState
/* 创建上传记录失败 */
this.uploadState = this.createFailState
/* 文件上传中 */
this.uploadState = this.uploadingState
/* 文件上传成功 */
this.uploadState = this.uploadSucceedState
/* 文件上传失败 */
this.uploadState = this.uploadErrorState
/* 将删除事件委托给当前状态类 */
Upload.prototype.delete = function () {
this.uploadState.delete()
}
5、使用
let uploader = new Upload();
/* 删除文件 */
uploader.delete()
重构结束。