大话状态模式

「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战

介绍

很多公司都要求尽可能的减少if-else这些条件分支语句,或许你就会用到状态模式,本期会跟大家聊聊状态模式的那些事,它也许是解决某些需求场景的最好方法,会给你的业务逻辑带来巨大的好处。如果了解游戏开发游戏的小伙伴,在开发游戏人物状态的时候,往往会用到有限状态机,它就是一种状态模式的案例。

举个生活中的栗子,我们小时候在炎热的夏天有种老式的风扇,有三个档,第一次按就会开启风扇的一档,再按就会从一档升到二挡,再按就是二挡升到三挡,到了三挡再按就变成关闭风扇了。如果我们用代码写这个风扇的逻辑,会频繁的使用if-else这些条件分支语句,随着条件的增多,我们维护和查阅的难度会越来越大,接下来,我们会在案例当中来通过状态模式来解决这个问题。

概念

状态模式是一种行为型模式。它解决了对象的行为依赖于它的状态,并且可以根据它的状态改变而改变它的相关行为。

案例

var FSM = {
	off:{
		name:"关闭",
		action:function(){
			console.log("触发按钮了,当前状态由"+this.currState.name+"进行改变为一级风");
		},
		change:function(){
			this.currState = this.state.lv1;
			return '开启一级风速'
		}
	},
	lv1:{
		name:"开启一级风速",
		action:function(){
			console.log("触发按钮了,当前状态由"+this.currState.name+"进行改变为二级风");
		},
		change:function(){
			this.currState = this.state.lv2;
			return '开启二级风速'
		}
	},
	lv2:{
		name:"开启二级风速",
		action:function(){
			console.log("触发按钮了,当前状态由"+this.currState.name+"进行改变为三级风");
		},
		change:function(){
			this.currState = this.state.lv3;
			return '开启三级风速'
		}
	},
	lv3:{
		name:"开启三级风速",
		action:function(){
			console.log("触发按钮了,当前状态由"+this.currState.name+"进行改变为关闭");
		},
		change:function(){
			this.currState = this.state.off;
			return '关闭'
		}
	}
}

var delegate = function(client,delegation){
	return {
		name: delegation.name,
		change:function(){
			return delegation.change.apply(client,arguments);
		},
		action:function(){
			return delegation.action.apply(client,arguments);
		}
	}
}

var Fan = function(){
	this.state = {
		off:delegate(this,FSM.off),
		lv1:delegate(this,FSM.lv1),
		lv2:delegate(this,FSM.lv2),
	    lv3:delegate(this,FSM.lv3)
	}
	this.currState = this.state.off;
}
Fan.prototype.setCurrState = function(stateName){
	this.currState = this.state[stateName] || this.state.off;
	return this;
}
Fan.prototype.init = function(){
	var btn = document.createElement("button");
	var msg = document.createElement("span");
	msg.style.marginLeft = "15px";
	btn.innerHTML = "风扇按钮";
	msg.innerHTML = "状态:"+ this.currState.name;
	btn.addEventListener("click",function(e){
		this.currState.action.apply(this);
		var ret = this.currState.change.apply(this);
		msg.innerHTML = "状态:"+ret;
	}.bind(this),false);
	document.body.appendChild(btn);
	document.body.appendChild(msg);
}
复制代码

这里我们在一个FSM对象里写了几种状态,其中包含了状态名,行为,以及改变的新的状态。

为了方便通过delegate让他们统一管理他们。

Fan类的state就是相应给予的状态种类,setCurrState可以直接设置状态。而init作为初始化,为了方便我们直接在js当中创建dom。

然后,我们实例化一下Fan类,初始设置为关闭状态。

var fan = new Fan();
fan.setCurrState("off").init();
复制代码

接下来就是见证奇迹的时刻了~

VID_20220210_220617.gif

这样,我们就可以通过它去代替丑陋的if-else,以后加多少种状态都会一目了然。

优点

  1. 封装了转换规则,使之更清晰,而且允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块,可维护性增加。
  2. 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  3. 可以让多个环境对象共享一个状态对象,从而减少系统中对象的数目。

缺点

  1. 状态模式的使用必然会增加系统类和对象的个数。
  2. 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  3. 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

使用场景

  1. 行为随状态改变而改变的场景
  2. 代替条件分支语句的逻辑。

结语

状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变。它可以使你的很多杂乱无章的代码会变得清晰。

Guess you like

Origin juejin.im/post/7063111503720742943