小程序设计模式之状态模式-蓝牙配网
引言
相信大家在开发的过程中或多或少遇到过某些事件存在多种状态,不知道大家都是怎么处理的,是不是需要花大量时间去维护这些状态。随着物联网的普及,家电等产品的配网功能也越发普及。本文就以微信小程序蓝牙配网如何使用状态模式,因为配网涉及到多个状态。
蓝牙配网
整个过程一般就是用户通过小程序搜索到设备,然后通过蓝牙连接上设备,再把家中的路由器信息发送给设备,最后设备通过收到的信息连上路由器,接入网络。
状态机
我们先先来看看一般的做法
① 首先,我们找出蓝牙配网中的一些状态:
1. 蓝牙未连接
2. 蓝牙连接中
3. 蓝牙已连接
4. 路由信息已接收
5. WIFI连网中
6. 配网成功
② 然后,定义一些常量来指定各个状态:
const UNCONNECTED = 1;
const CONNECTING = 2;
const CONNECTED = 3;
const RECEIVED = 4;
const WIFI_CONNECTING = 5;
const WIFI_CONNECTED = 6;
let state = UNCONNECTED;
③ 最后我们创建一个状态机的类,比如在连接的函数里处理每个状态下的情况:
④ 使用状态机的类
这看起来没什么问题,也挺好用。但某次会议上,某人提出了现在的方式有缺陷,不是很安全,应该在蓝牙连接后进行设备的认证。最后领导拍板,在原来的基础上增加几个状态。
既然要更改,那就赶紧召集开发人员,结果开发人员一看,沃日要修改好多地方:
1.首先可能要增加两种新的状态:
const AUTHENTCATING = 7;
const AUTHENTCATED = 8;
2.然后在那些处理状态的方法里增加类似else if的东西,太多了,太多了。
就在程序员炸裂的边缘,你们的架构师瞄了眼说,怎么没有用状态模式,程序员一脸无奈的看着架构师。
好了题外话说到这,进入本次的正题——状态模式
什么是模式
模式是指在某种情境下,针对某问题的某种解决方案。
何时用设计模式
在设计的时候,如果遇到某个问题,而且该问题不能用简单的设计就能解决,那么可以试着利用某个模式帮助我们解决那个问题。
新的设计
我们把每个状态都看成一个对象,然后在动作发生时委托给当前状态。
首先,我们定义个抽象类State,在这个类内,蓝牙配网的每个动作都有一个对应的函数。
然后为每个状态实现状态类。这些类将负责对应状态下的行为。
最后,将动作委托到状态类。
重新定义State类
class AbstractState {
constructor(stateController) {
this.stateController = stateController; // 状态控制类
}
/**
* 连接蓝牙设备
* @see UnConnectState
*/
connect() {}
/**
* 断开蓝牙设备
* @see UnConnectState
*/
disconnect() {}
/**
* 蓝牙连接成功
* @see UnConnectState
*/
connectSuccess() {}
/**
* 蓝牙连接失败
* @see UnConnectState
*/
connectFail() {}
/**
* 路由数据接收完成
* @see ReceivedState
*/
received() {}
/**
* 认证中
* @see AuthenticatingState
*/
authenticating() {}
/**
* 认证完成
* @see AuthenticatedState
*/
authenticated() {}
/**
* 充电完成
* @see WifiConnectingState
*/
wifiConnecting() {}
/**
* 时间同步完成
* @see WifiConnectedState
*/
wifiConnected() {}
/**
* 获取状态ID
*/
getStateId() {
return 0;
}
/**
* 释放资源
* @see ConnectedState
*/
onRelease() {}
}
module.exports = AbstractState
实现各状态类
UnConnectState类
import AbstractState from './AbstractState'
class UnConnectState extends AbstractState { // 未连接状态只处理连接的事件
constructor(stateController) {
super(stateController);
}
/**
* @override
* @description 连接设备
*/
connect() { // 这里只是实例,具体需要怎么处理还要更具体的业务逻辑
const ble = this.stateController.getBLE();
if (ble) {
this.stateController.setState(this.stateController.getConnectingState());
this.stateController.notifyAll();
ble.initBleAdapter();
}
}
getStateId() {
return 100;
}
}
module.exports = UnConnectState
ConnectingState类
import AbstractState from './AbstractState'
class ConnectingState extends AbstractState { // 蓝牙连接状态的类
constructor(stateController) {
super(stateController);
}
connectSuccess() {
this.stateController.setState(this.stateController.getConnectedState());
this.stateController.notifyAll();
}
connectFail() {
this.stateController.setState(this.stateController.getUnConnectState());
this.stateController.notifyAll();
}
disconnect() {
this.stateController.setState(this.stateController.getUnConnectState());
this.stateController.notifyAll();
}
getStateId() {
return 101;
}
}
module.exports = ConnectingState
ConnectedState类
import AbstractState from './AbstractState'
class ConnectedState extends AbstractState {
constructor(stateController) {
super(stateController);
}
connectFail() {
this.stateController.setState(this.stateController.getUnConnectState());
this.stateController.notifyAll();
// 结束发送路由的命令
}
authenticating() { this.stateController.setState(this.stateController.getAuthenticatingState());
// 发送认证的指令
}
disconnect() {
this.stateController.setState(this.stateController.getUnConnectState());
this.stateController.notifyAll();
}
getStateId() {
return 102;
}
onRelease() {
}
}
module.exports = ConnectedState
由于篇幅,其他的状态类就不在这里写了。
StateController类
import UnConnectState from './UnConnectState';
import ConnectingState from './ConnectingState';
import ConnectedState from './ConnectedState';
const DEBUG = true;
const TAG = 'StateController#';
let mUnConnectState = null;
let mConnectingState = null;
let mConnectedState = null;
let mState = null;
let mBle = null;
let STATE_CONTROLLER = null;
/** 页面监听器列表 */
let mPageObservers = new Set();
class StateController {
constructor() {
mUnConnectState = new UnConnectState(this);
mConnectingState = new ConnectingState(this);
mConnectedState = new ConnectedState(this);
mState = mUnConnectState;
}
static getInstance() {
if (STATE_CONTROLLER == null) {
STATE_CONTROLLER = new StateController();
}
return STATE_CONTROLLER;
}
addPage(page) {
mPageObservers.add(page);
}
deletePage(page) {
mPageObservers.delete(page);
}
clearPages() {
mPageObservers.clear();
}
/**
* 连接设备,在调用之前确保已经调用过#setDeviceId(deviceId)
*/
connect() {
mState.connect();
}
/**
* 断开设备连接
*/
disconnect() {
mState.disconnect();
}
/**
* 设备连接成功
*/
connectSuccess() {
mState.connectSuccess();
}
/**
* 设备连接失败
*/
connectFail() {
mState.connectFail();
}
/**
* 设置状态
* @param {*} state 需要设置的状态
* @see UnConnectState
* @see ConnectingState
* @see ConnectedState
*/
setState(state) {
mState = state;
}
/**
* 释放状态资源
*/
onRelease() {
mState.onRelease();
}
/**
* 页面退出
*/
onUnload() {
this.disconnect();
this.onRelease();
}
getState() {
return mState;
}
getUnConnectState() {
return mUnConnectState;
}
getConnectingState() {
return mConnectingState;
}
getConnectedState() {
return mConnectedState;
}
getStateId() {
return mState.getStateId();
}
getBLE() {
if (mBle == null)
mBle = BLE.getInstance();
return mBle;
}
notifyAll() {
this.notifyState(mState.getStateId());
}
/**
* 通知页面当前的状态
* @param {*} status
*/
notifyState(status) {
mPageObservers.forEach((pageObserver) => {
pageObserver(status);
});
}
}
module.exports = StateController
使用新的设计
import State from './state'
import StateController from './StateController'
class StateTest {
constructor() {}
onClick() {
// let state = new State();
// state.connectBLE();
let stateController = StateController.getInstance();
stateController.connect();
}
}
module.exports = StateTest;
通过新的设计,我们可以看到一些变化:
1.将每个状态的行为局部化到了它自己的类中;
2.去掉了很多的ifelse语言;
3.方便扩展,可增删状态,对每个状态来说是“对修改关闭”。
状态模式定义
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
状态模式类图
总结
- 状态模式用类代表状态;
- 使用状态模式会增加类的数目,但这是为了更好的扩展和弹性;
- 状态的转换可以由各状态(State)改变,也可以由Context类控制,比如示例中的StateController。
如需转载请注明原地址