本文首发于 vivo互联网技术 微信公众号
链接: 《状态机引擎在vivo营销自动化中的深度实践 | 引擎篇02》
作者:陈王荣
一、业务背景
营销自动化平台支持多种不同类型运营活动策略(比如:短信推送策略、微信图文推送策略、App Push推送策略),每种活动类型都有各自不同的执行流程和活动状态。比如短信活动的活动执行流程如下:
(图1-1:短信活动状态转移)
整个短信活动经历了 未开始 → 数据准备中 → 数据已就绪 → 活动推送中→ 活动结束 多个状态变更流程。不仅如此, 我们发现在活动业务逻辑处理过程中,都有以下类似的特点:
-
每增加一种新的活动业务类型,就要新增相应的活动状态以及处理状态变更的逻辑;
-
当一个类型的活动业务流程有修改时,可能需要对原先的状态转移过程进行变更;
-
当每个业务都各自编写自己的状态转移的业务代码时,核心业务逻辑和控制逻辑耦合性会非常强,扩展性差,成本也很高。
针对系统状态的流转管理,计算机领域有一套标准的理论方案模型——有限状态机。
二、理解状态机
2.1 状态机定义
有限状态机(Finite-State Machine , 缩写:FSM),简称状态机。是表示有限个状态以及这些状态之间的转移和触发动作的模型。
-
状态是描述系统对象在某个时刻所处的状况。
-
转移指示状态变更,一般是通过外部事件为条件触发状态的转移。
-
动作是对给定状态下要进行的操作。
简而言之,状态机是由事件、状态、动作三大部分组成。三者的关系是:事件触发状态的转移,状态的转移触发后续动作的执行。其中动作不是必须的,也可以只进行状态转移,不进行任何操作。
(图2-1:状态机组成)
所以将上述【图1-1:短信活动状态转移 】使用状态机模型来描述就是:
(图2-2:短信活动状态机)
状态机本质上是对系统的一种数学建模,将问题解决方案系统化表达出来。下面我们来看下在实际开发中有哪些实现状态机的方式 。
2.2 状态机的实现方式
2.2.1 基于条件判断的实现
这是最直接的一种实现方式,所谓条件判断就是通过使用 if-else 或 switch-case 分支判断进行硬编码实现。对于前面短信活动,基于条件判断方式的代码实例如下:
/**
* 短信活动状态枚举
*/
public enum ActivityState {
NOT_START(0), //活动未开始
DATA_PREPARING(1), //数据准备中
DATA_PREPARED(2), //数据已就绪
DATA_PUSHING(3), //活动推送中
FINISHED(4); //活动结束
}
/**
* 短信活动状态机
*/
public class ActivityStateMachine {
//活动状态
private ActivityState currentState;
public ActivityStateMachine() {
this.currentState = ActivityState.NOT_START;
}
/**
* 活动时间开始
*/
public void begin() {
if (currentState.equals(ActivityState.NOT_START)) {
this.currentState = ActivityState.DATA_PREPARING;
//发送通知给运营人员
notice();
}
// do nothing or throw exception ...
}
/**
* 数据计算完成
*/
public void finishCalData() {
if (currentState.equals(ActivityState.DATA_PREPARING)) {
this.currentState = ActivityState.DATA_PREPARED;
//发送通知给运营人员
notice();
}
// do nothing or throw exception ...
}
/**
* 活动推送开始
*/
public void beginPushData() {
//省略
}
/**
* 数据推送完成
*/
public void finishPushData() {
//省略
}
}
通过条件分支判断来控制状态的转移和动作的触发,上述的 if 判断条件也可以换成 switch 语句,以当前状态为分支来控制该状态下可以执行的操作。
适用场景
适用于业务状态个数少或者状态间跳转逻辑比较简单的场景。
缺陷
当触发事件和业务状态之间对应关系不是简单的一对一时,就需要嵌套多个条件分支判断,分支逻辑会变得异常复杂;当状态流程有变更时,也需要改动分支逻辑,不符合开闭原则,代码可读性和扩展性非常差。
2.2.2 基于状态模式的实现
了解设计模式的童鞋,很容易就可以把状态机和状态模式这两个概念联系起来,状态模式其实可以作为状态机的一种实现方式。主要实现思路是通过状态模式将不同状态的行为进行分离,根据状态变量的变化,来调用不同状态下对应的不同方法。代码示例如下:
/**
* 活动状态接口
*/
interface IActivityState {
ActivityState getName();
//触发事件
void begin();
void finishCalData();
void beginPushData();
void finishPushData();
}
/**
* 具体状态类—活动未开始状态
*/
public class ActivityNotStartState implements IActivityState {
private ActivityStateMachine stateMachine;
public ActivityNotStartState(ActivityStateMachine stateMachine) {
this.stateMachine = stateMachine;
}
@Override
public ActivityState getName() {
return ActivityState.NOT_START;
}
@Override
public void begin() {
stateMachine.setCurrentState(new ActivityDataPreparingState(stateMachine));
//发送通知
notice();
}
@Override
public void finishCalData() {
// do nothing or throw exception ...
}
@Override
public void beginPushData() {
// do nothing or throw exception ...
}
@Override
public void finishPushData() {
// do nothing or throw exception ...
}
}
/**
* 具体状态类—数据准备中状态
*/
public class ActivityDataPreparingState implements IActivityState {
private ActivityStateMachine stateMachine;
public ActivityNotStartState(ActivityStateMachine stateMachine) {
this.stateMachine = stateMachine;
}