状态模式
在我们开发过程当中,我们经常会碰到这样一个场景,一个对象有多个状态,每次状态发生变化,他的行为或者逻辑就会发生改变,这个时候我们往往要面对复杂的判断逻辑。传统的方式是用if-else或者swtich-case把所有可能考虑到的情况都写进去,这样会导致代码很臃肿而去逻辑会越来越复杂。
定义
当对象改变状态时,其行为也跟着变化,看起来就像变了一个类。
状态模式的核心就是封装,状态发生变更引起了行为变化,从外部看就像是对象对应的类发生了变化一样。所以我们只需要关心他的状态。
例子
这边举一个电梯的例子,电梯一般有四个状态,运行、停止、开门、关门,这四个状态互相交织,互相有影响,比如开门的状态下就只允许关门的动作,而停止的状态下允许进行开门、关门或者运行的动作。如果采用传统的if-else判断的话会变成这样:
let lift = {
state: 'open',
open: function() {
if(this.state==='open'||this.state==='run'){
console.log('不允许开门')
}else if(this.state==='stop'||this.state==='close'){
console.log('开门')
}
},
close: function() {
//...
}
}
这样不仅要重复做逻辑判断,会让代码十分臃肿。而且在将来添加状态后,会难以维护。
/*电梯类*/
class Lift {
constructor() {
this.openState = new OpenLiftState(this);
this.closeState = new CloseLiftState(this);
this.runState = new RunLiftState(this);
this.stopState = new StopLiftState(this);
this.nowState = this.stopState; // 初始的状态
}
open() {
// 实际上调用的是状态类的方法
this.nowState.open()
}
close() {
this.nowState.close();
}
run() {
this.nowState.run();
}
stop() {
this.nowState.stop();
}
}
/**
* 电梯基本状态类
* 提供电梯的基本状态下的方法
*/
class LiftState {
constructor(Lift) {
// 状态类拿到电梯实例
this.Lift = Lift;
}
/**
* 状态改变方法
* 把nowstate类直接变成对应状态类
*/
setState(state){
switch(state) {
case 'close': this.Lift.nowState = this.Lift.closeState; break;
case 'open': this.Lift.nowState = this.Lift.openState; break;
case 'run': this.Lift.nowState = this.Lift.runState; break;
case 'stop': this.Lift.nowState = this.Lift.stopState; break;
}
}
open(){
console.log('error:当前状态不允许开门');
}
run(){
console.log('error:当前状态不允许运行');
}
close(){
console.log('error:当前状态不允许关门');
}
stop(){
console.log('error:当前状态不允许停止');
}
}
class OpenLiftState extends LiftState {
constructor(props){
super(props)
}
/*开门的状态下只允许关门,所以我们重写开门的方法*/
close() {
console.log('已关门');
this.setState('close');
}
}
class CloseLiftState extends LiftState {
constructor(props){
super(props);
}
/*关门状态下允许开门、运行、停止*/
open(){
console.log('已开门');
this.setState('open');
}
run(){
console.log('已运行');
this.setState('run');
}
stop(){
console.log('已停止');
this.setState('stop');
}
}
class RunLiftState extends LiftState {
constructor(props){
super(props);
}
/*运行状态下只允许停止*/
stop(){
console.log('已停止');
this.setState('stop');
}
}
class StopLiftState extends LiftState {
constructor(props){
super(props);
}
/*停止状态下允许开门、运行*/
open(){
console.log('已开门');
this.setState('open');
}
run(){
console.log('已运行');
this.setState('run');
}
}
/*测试*/
let lift = new Lift(); // 初始状态为stop
lift.open(); // 开门
lift.run(); // 不允许运行
lift.stop(); // 不允许停止
lift.close(); // 关门
lift.stop(); // 停止
这样一来,我们只需要关注每个状态所能执行的动作,从而摆脱了复杂的逻辑判断。在将来,如果你想要添加一个新的状态,比如说故障,你就可以新增一个故障的状态类,这样就可以十分轻易的扩展你的类。
应用场景
组件的设计模式
在组件的开发过程当中,我们可以很好的应用到这个思想。当你的组件的行为受到状态的控制时,你就可以通过状态模式进行设计。这样设计出来的组件结构更清晰,也更易于扩展。但是这样的设计模式适合于状态较少的情况,当状态比较多的情况下,最好进行拆分成小组件后再用状态模式的思路进行设计。