一、简单介绍
状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变。
总的来说,状态模式就是封装状态,允许一个对象在其内部状态改变时改变它的行为,我们之前一般都是封装行为。
二、使用场景
在一些场景中,我们会定义一些状态,根据状态去执行不同的操作。比如,一个表单,初始状态有个编辑按钮,表单是只读的。点了编辑之后,表单变为可编辑,同时编辑按钮消失,出现了保存和取消按钮。点保存或者取消之后,回到初始状态。又或者,有一个开关控制关闭状态,点一下,变为打开状态;再点一下,变为关闭状态。
传统做法,我们是在点击事件里面写if else,但是,如果状态的种类增加了怎么办?我们需要修改原来的方法,去多写一些条件分支,同时增加对应的处理逻辑,这样显得不是很优雅。所以,状态模式就派上用场了。
三、示例
假设有这么一个场景。我们在界面有两个按钮,第一个按钮表示状态的切换,每点击一次,进入下一个状态。第二个方法点击之后执行对应的逻辑。假设我们现在4个状态,按钮背景颜色对应是黑色,透明度为0,0.3,0.6,1.以vue为例,来写一下大概的逻辑。
<template>
<div>
<div class="state-button" :style="getStyle" @click="state.changeState">切换状态</div>
<a-button class="state" @click="state.handle">执行逻辑</a-button>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import { getWeekdayConfig } from "./http";
interface StateHandle {
value: number;
handle: Function;
changeState: Function;
}
@Component({})
export default class State extends Vue {
public changeState(state: keyof State) {
this.state = this[state];
}
public firstHandler() {
//状态逻辑
console.log("satate first");
}
public secondHandler() {
//状态逻辑
console.log("satate second");
}
public thirdHandler() {
//状态逻辑
console.log("satate third");
}
public forthHandler() {
//状态逻辑
console.log("satate forth");
}
public firstState: StateHandle = {
value: 0,
handle: this.firstHandler,
changeState: this.changeState.bind(this, "secondState")
};
public secondState: StateHandle = {
value: 0.3,
handle: this.secondHandler,
changeState: this.changeState.bind(this, "thirdState")
};
public thirdState: StateHandle = {
value: 0.6,
handle: this.thirdHandler,
changeState: this.changeState.bind(this, "forthState")
};
public forthState: StateHandle = {
value: 1,
handle: this.forthHandler,
changeState: this.changeState.bind(this, "firstState")
};
// public changeState() {}
public state: StateHandle = this.firstState;
public get getStyle() {
return {
background: `rgba(0,0,0,${this.state.value})`
};
}
}
</script>
<style lang="less" scoped>
.state-button {
display: inline-block;
padding: 5px 12px;
border: 1px solid #eee;
box-sizing: border-box;
cursor: pointer;
}
</style>
在上面的示例中,我们定义了四个状态,每个状态都有相同的接口,并把状态切换的功能放在了状态对象内部。我们需要执行的逻辑也放在各自的状态对象内部。这样一来,每个状态自己的逻辑和属性就维护在状态内部了,状态切换的顺序也由相关状态对象自己来控制。
如果是在普通项目里,我们可以把所有状态放在一个大的对象里,这个对象称作我们的状态机,我们需要做的就是定义好我们的状态机。
四、拓展
状态模式和之前提到的策略模式很相似,都是根据条件的不同,去执行对应的逻辑。它们的区别是,策略模式中的各个策略类,或者说条件,是互相独立且平行的,各个条件之间没有直接的关系,我们可以随时切换条件,在状态模式中,各个状态之间的切换规则是开始就被定义好的。改变状态发生在各个状态类的内部,一般的话不应该在外部去修改我们维护的状态。