订单及其状态机的设计实现

订单及其状态机的设计实现

 状态机简介:

 

状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。【规则的抽象】

有限状态机一般都有以下特点:

(1)可以用状态来描述事物,并且任一时刻,事物总是处于一种状态;

(2)事物拥有的状态总数是有限的;

(3)通过触发事物的某些行为,可以导致事物从一种状态过渡到另一种状态;

(4)事物状态变化是有规则的,A状态可以变换到B,B可以变换到C,A却不一定能变换到C;

(5)同一种行为,可以将事物从多种状态变成同种状态,但是不能从同种状态变成多种状态。

 

状态机这种描述客观世界的方式就是将事物抽象成若干状态,然后所有的事件和规则导致事物在这些状态中游走。最终使得事物“自圆其说”。

很多通信协议的开发都必须用到状态机;一个健壮的状态机可以让你的程序,不论发生何种突发事件都不会突然进入一个不可预知的程序分支。

 

  • 状态机示例:

 

 

 

四大概念:

状态(state)

一个状态机至少要包含两个状态。

分为:现态(源状态)、次态(目标状态)

状态可以理解为一种结果,一种稳态形式,没有扰动会保持不变的。

 

状态命名形式:

1.副词+动词;例如:待审批、待支付、待收货

这种命名方式体现了:状态机就是事件触发状态不断迁徙的本质。表达一种待触发的感觉。

2.动词+结果;例如:审批完成、支付完成

3.已+动词形式;例如:已发货、已付款

以上两种命名方式体现了:状态是一种结果或者稳态的本质。表达了一种已完成的感觉。

 

角色很多的时候,为了表示清晰,可以加上角色名:例如:待财务审批、主管已批准 

命名考虑从用户好理解角度出发。

 

事件(event)

or

触发条件

又称为“条件”,就是某个操作动作的触发条件或者口令。当一个条件满足时,就会触发一个动作,或者执行一次状态迁徙

这个事件可以是外部调用、监听到消息、或者各种定时到期等触发的事件。

对于灯泡,“打开开关”就是一个事件。

条件命名形式:动词+结果;例如:支付成功、下单时间>5分钟

动作(action)

事件发生以后要执行动作。例如:事件=“打开开关指令”,动作=“开灯”。一般就对应一个函数。

条件满足后执行动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。

动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

那么如何区分“动作”和“状态”?

“动作”是不稳定的,即使没有条件的触发,“动作”一旦执行完毕就结束了;

而“状态”是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。

 变换(transition)

即从一个状态变化到另外一个状态

例如:“开灯过程”就是一个变化

 

状态机其他表达方式:

 

 

状态机的设计:

 

信息系统中有很多状态机,例如:业务订单的状态。 

状态机的设计存在的问题:什么是状态?到底有多少个状态?要细分到什么程度?

  信息系统是现实世界一种抽象和描述。而业务领域中那些已经发生的事件就是事实信息系统就是将这些事实以信息的形式存储到数据库中,即:信息就是一组事实

信息系统就是存储这些事实,对这些事实进行管理与追踪,进而起到提供提高工作效率的作用。

信息系统就是记录已经发生的事实,信息系统中的状态机基本和事实匹配。即:标识某个事实的完成度。

业务系统,根据实际业务,具体会有哪些发生的事实需要记录,基本这些事实就至少对应一个状态。需要记录的事实就是一种稳态,一种结果。

例如:【待支付】->【已支付】->【已收货】->【已评价】

这些都是系统需要记录的已发生的客观事实。而这些事实就对应了状态,而发生这些事实的事件就对应了触发状态机的转换的事件。

根据自己的业务实际进行分析,并画出状态图即可。

 

状态机实现方式:状态模式

 

下面使经典的自动贩卖机例子来说明状态模式的用法,状态图如下:

 

 

 

 

 

 分析一个这个状态图:

a、包含4个状态(我们使用4个int型常量来表示)
b、包含3个暴露在外的方法(投币、退币、转动手柄、(发货动作是内部方法,售卖机未对外提供方法,售卖机自动调用))
c、我们需要处理每个状态下,用户都可以触发这三个动作。

 

我们可以做没有意义的事情,在【未投币】状态,试着退币,或者同时投币两枚,此时机器会提示我们不能这么做。

实现逻辑:

    任何一个可能的动作,我们都要检查,看看我们所处的状态和动作是否合适。

/**
 * 自动售货机
 * 
 * 
 */
public class VendingMachine
{
 
    /**
     * 已投币
     */
    private final static int HAS_MONEY = 0;
    /**
     * 未投币
     */
    private final static int NO_MONEY = 1;
    /**
     * 售出商品
     */
    private final static int SOLD = 2;
    /**
     * 商品售罄
     */
    private final static int SOLD_OUT = 3;
    /**
     * 商品数量
     */
    private int count = 0;
    
         // 当前状态,开机模式是没钱
    private int currentStatus = NO_MONEY;
 
        // 开机设置商品数量,初始化状态
    public VendingMachine(int count)
    {
        this.count = count;
        if (count > 0)
        {
            currentStatus = NO_MONEY;
        }
    }
 
    /**
     * 投入硬币,任何状态用户都可能投币
     */
    public void insertMoney()
    {
        switch (currentStatus)
        {
        case NO_MONEY:
            currentStatus = HAS_MONEY;
            System.out.println("成功投入硬币");
            break;
        case HAS_MONEY:
            System.out.println("已经有硬币,无需投币");
            break;
        case SOLD:
            System.out.println("请稍等...");
            break;
        case SOLD_OUT:
            System.out.println("商品已经售罄,请勿投币");
            break;
 
    }
    }
 
    /**
     * 退币,任何状态用户都可能退币
     */
    public void backMoney()
    {
        switch (currentStatus)
        {
        case NO_MONEY:
            System.out.println("您未投入硬币");
            break;
        case HAS_MONEY:
            currentStatus = NO_MONEY;
            System.out.println("退币成功");
            break;
        case SOLD:
            System.out.println("您已经买了糖果...");
            break;
        case SOLD_OUT:
            System.out.println("您未投币...");
            break;
        }
    }
 
    /**
     * 转动手柄购买,任何状态用户都可能转动手柄
     */
    public void turnCrank()
    {
        switch (currentStatus)
        {
        case NO_MONEY:
            System.out.println("请先投入硬币");
            break;
        case HAS_MONEY:
            System.out.println("正在出商品....");
            currentStatus = SOLD;
            dispense();
            break;
        case SOLD:
            System.out.println("连续转动也没用...");
            break;
        case SOLD_OUT:
            System.out.println("商品已经售罄");
            break;
 
    }
    }
 
    /**
     * 发放商品
     */
    private void dispense()
    {
 
    switch (currentStatus)
    {
    case NO_MONEY:
    case HAS_MONEY:
    case SOLD_OUT:
        throw new IllegalStateException("非法的状态...");
    case SOLD:
        count--;
        System.out.println("发出商品...");
        if (count == 0)
        {
            System.out.println("商品售罄");
            currentStatus = SOLD_OUT;
        } else
        {
            currentStatus = NO_MONEY;
        }
        break;
 
    }
 
    }
}
hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » 订单及其状态机的设计实现