18-设计模式-状态模式(设计模式学习笔记)

状态模式

状态模式.png

状态模式用于解决系统中复杂对象的状态转换,以及不同状态下的封装问题。定义一个对象 ,对象管理状态,使程序做出相应的变化。

当有大量if…else…或者switch…case这些分支语句的时候,如:

if(a){
    // 每增加一种状态,就要修改一次原来的代码
}else if(b){
    //      
}else if(c){
    //      
}else if(d){
    //      
}else{
    //
}
复制代码

有大量的状态,不便于管理的时候,并且后期每增加一种状态,就要修改原来的代码(不符合开闭原则),此时就可以使用状态模式。

状态模式主要由2个角色组成

状态类:管理状态

环境类:改变其状态

示例一

比如一个人在生活中,遇到不同的环境,会做不同的事情,他开心的时候会做什么,伤心的时候会做什么,生气的时候又会做什么…

// 状态类 抽象的
class State{
    toDo(){
        console.log('不同状态做不同事情');
    }
}

// 使用状态模式,后续每增加一种状态,都无需修改上面的源代码,直接在下面添加即可
// 开心的状态
class Happy extends State{
   toDo(){
     console.log("开心的时候大吃一顿");
   }
}

// 伤心的状态
class UnHappy extends State{
   toDo(){
     console.log("伤心的时候大哭一场");
   }
}

// 生气的状态
class Angry extends State{
   toDo(){
     console.log("生气的时候大吵大闹");
   }
}

// 环境类 不同的生活环境会造成不同状态
class Life{
   changeState(state){ // 接收一个状态的实例
     state.toDo(); // 执行相应状态实例中对应的行为
   }
}

// 实例化生活环境
let life = new Life();

// 传入开心
life.changeState(new Happy()); // 开心的时候大吃一顿

// 传入伤心
life.changeState(new UnHappy()); // 伤心的时候大哭一场

// 传入生气
life.changeState(new Angry()); // 生气的时候大吵大闹
复制代码

示例二

状态模式示例二.png

现在用代码来描述这个场景,首先定义一个Light 类,可以预见,电灯对象light 将从Light类创建而出, light 对象将拥有两个属性,我们用state 来记录电灯当前的状态,用button 表示具体的开关按钮。

var Light = function(){
    this.state = 'off'; // 给电灯设置初始状态off
    this.button = null; // 电灯开关按钮
};

Light.prototype.init = function(){
    var button = document.createElement( 'button' ),
        self = this;
    button.innerHTML = '开关';
    this.button = document.body.appendChild( button );
    this.button.onclick = function(){
        self.buttonWasPressed();
    }
};

Light.prototype.buttonWasPressed = function(){
    if ( this.state === 'off' ){
        console.log( '开灯' );
        this.state = 'on';
    }else if ( this.state === 'on' ){
        console.log( '关灯' );
        this.state = 'off';
    }
};

var light = new Light();

light.init();

复制代码

上面这段代码逻辑十分清晰, 只是灯泡可能有很多种, 有些灯泡的开关并不是只有开和关两个状态, 还有第一次按,第二次按, 第三次按等等, 每次效果都不一样.如果只是单纯的堆砌if else的话, 是没法完成我们要的效果的

针对更复杂的情况, 我们就得再去改造一下我们的代码

通常我们谈到封装,一般都会优先封装对象的行为,而不是对象的状态。但在状态模式中刚好相反,状态模式的关键是把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部

我们接下来实现一个新的业务需求: 第一次按下打开弱光,第二次按下打开强光,第三次才是关闭电灯

// OffLightState:
var OffLightState = function( light ){
    this.light = light;
};

OffLightState.prototype.buttonWasPressed = function(){
    console.log( '弱光' ); // offLightState 对应的行为
    this.light.setState( this.light.weakLightState ); // 切换状态到weakLightState
};

// WeakLightState:
var WeakLightState = function( light ){
    this.light = light;
};

WeakLightState.prototype.buttonWasPressed = function(){
    console.log( '强光' ); // weakLightState 对应的行为
    this.light.setState( this.light.strongLightState ); // 切换状态到strongLightState
};

// StrongLightState:
var StrongLightState = function( light ){
    this.light = light;
};

StrongLightState.prototype.buttonWasPressed = function(){
    console.log( '关灯' ); // strongLightState 对应的行为
    this.light.setState( this.light.offLightState ); // 切换状态到offLightState
};

var Light = function(){
    this.offLightState = new OffLightState( this );
    this.weakLightState = new WeakLightState( this );
    this.strongLightState = new StrongLightState( this );
    this.button = null;
};

Light.prototype.init = function(){
    var button = document.createElement( 'button' ),
        self = this; 
    this.button = document.body.appendChild( button );
    this.button.innerHTML = '开关';
    this.currState = this.offLightState; // 设置当前状态
    this.button.onclick = function(){
        self.currState.buttonWasPressed();
    }
};

Light.prototype.setState = function( newState ){
    this.currState = newState;
};

var light = new Light();

light.init();
复制代码

使用状态模式的好处很明显,它可以使每一种状态和它对应的行为之间的关系局部化,这些行为被分散和封装在各自对应的状态类之中,便于阅读和管理代码。

另外,状态之间的切换都被分布在状态类内部,这使得我们无需编写过多的if、else 条件分支语言来控制状态之间的转换。

当我们需要为light 对象增加一种新的状态时,只需要增加一个新的状态类,再稍稍改变一些现有的代码即可。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享