SMC(the State Machine Compiler) 静态状态机

946 阅读4分钟

今天主要介绍状态机生成工具SMC具体如何使用

SMC的用法综述

状态机是什么我就不赘述了,感兴趣的可以自行搜索文章了解一下 SMC的用法简单来说 就是用一个想成的jar包 + 一份sm文件(按到sm语法写的,规定了状态有哪些,状态怎么流转等等) ----> 用jar包执行 就会生成一个java类, 将java类放入项目中, 按照要求使用就实现了一个状态机.

大概步骤如下

1. 准备两个jar包

statemap.jar是用来作为本地依赖添加到依赖中

smc.jar用来将sm文件编译成java文件

2. idea添加SMC 的plugin

新建sm文件 test.sm 按照语法编写


%class TestSMC  // 与该sm文件关联的接口类
%fsmclass TestStatusMachine // 生成后的java类名

%access public
%start TestStatus_Name::LOCKED  // LOCKED是状态机的开始状态

%map TestStatus_Name %%

    // 锁定状态
    LOCKED
    // Entry是别的状态跳转到当前状态时会执行的方法
    // 这里就是别的状态跳转到LOCKED时 会执行LOCKED的Entry方法
        Entry {
            refreshTestStatus(); // 可以在状态流转前后自定义方法
        }
    {

        TIME_OUT // 状态变化执行的操作
        EXPIRED { // 可能变成的状态

        }


    }

    EXPIRED 取消状态
        Entry {
            refreshTestStatus();
        }
    {

    }


%%

3. 执行命令生成java类

利用 java -jar Smc.jar -java -d temp test.sm 生成编译结果 temp是编译后生成的java类存的位置 会得到一个TestStatusMachine的java类

4. 对项目中相关实体类进行改造

(1) 对于包含状态的实体类进行改造

新建sm文件中标记了的接口类TestSMC

该接口定义了 状态流转前后可能需要执行的操作

public interface TestSMC {
    // 将状态机里的状态刷新到实体中
    void refreshTestStatus();
}

包含状态的实体类继承该接口

正常状态肯定是被某个实体类包含的, 例如订单实体就有个字段是状态,通常是枚举,代表了订单所处的状态,那么就是订单实体类继承TestSMC接口

public class TestOrder implements TestSMC {



    private Long orderId;
    private TestStatus status;

    // 状态机对象 就是第三步生成的java类
    private TestStatusMachine testStatusMachine;

    // 构造函数 新建该实体赋予实体一个初始状态
    private TestOrder(TestStatus testStatus) { //TestStatus 是状态的枚举类
        // 初始化状态机
        testStatusMachine = new TestStatusMachine(this, TestStatus.getTestSMCStatus());
        // 初始化订单状态
        this.status = testStatus;
    }
    
    /**
     * 接口方法的实现
     * 将状态机当前的状态 刷新到status字段
     * 由状态机自动完成更新
     */
    @Override
    public void refreshTestStatus () {
        if (this.getTestStatusMachine() != null) {
            String testStatusName = this.getTestStatusMachine().getState().getName().split("\\.")[1];
            this.status = TestStatus.valueOf(testStatusName);
        }
    }
 }

(2) 对于状态枚举类进行改造

public enum TestStatus {

    LOCKED(0, "已锁量") {
        @Override
        public TestSMCState getTestSMCStatus() {
            return TestStatus_Name.LOCKED;
        }
    },
    EXPIRED(8, "已过期") {
        @Override
        public TestSMCState getTestSMCStatus() {
            return TestStatus_Name.EXPIRED;
        }
    };


    private int code;
    private String desc;

    OrderStatus(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }

    // TestSMCState在自动生成的TestStatusMachine类里 每个枚举值都必须实现这个抽象方法
    public abstract TestSMCState getTestSMCStatus(); 
}

具体的用法

// 新建一个初始为LOCKED的对象
TestOrder testOrder = new TestOrder(TestStatus.LOCKED);
// 为该对象发生状态变化
testOrder.getTestStatusMachine().TIME_OUT();
// 状态变化必须通过状态机进行 因为TestOrder没有为status设置set方法.
// 注意TIME_OUT()是在sm文件中规定的LOCKED状态唯一能进行的操作

通过这种用法status 只能通过sm文件已经规定好的行为 流转到指定的状态, 如果有人想不符合规范的通过代码修改状态,是无法进行的,保证了项目的规范性,当然这一切是通过相应的约定才行的,毕竟状态机只是方便状态的管理, 控制它合理的流转而已

如果需要新增状态 或者增加相应的状态流转 需要通过修改sm文件 重新生成java类, 并且相应的实体也要按规范修改

生成sm文件状态流转的图片

第一步:

java -jar Smc.jar -graph -glevel 1 test.sm  

这个命令会生成一个test_sm.dot文件

第二步: 安装Graphviz后 可以将dot文件生成图片,类似下图

dot test_sm.dot -T png -o pic.png

官方文档(网上自行下载吧 好像上传不了)

SMC_Tutorial.pdf

SmcSrc_7_0_0.tgz