状态模式
状态模式用于解决系统中复杂对象的状态转换,以及不同状态下的封装问题。定义一个对象 ,对象管理状态,使程序做出相应的变化。
当有大量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()); // 生气的时候大吵大闹
复制代码
示例二
现在用代码来描述这个场景,首先定义一个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 对象增加一种新的状态时,只需要增加一个新的状态类,再稍稍改变一些现有的代码即可。