J2EE之SMC状态机配置及使用方法(by Lyon)

本文介绍SMC状态机配置工具的使用方法,包括如何根据配置文件生成代码及状态图,实现状态转换逻辑,特别关注状态机在地铁自动闸机场景的应用。

作者:进 Lyon,转载请注明作者名字以及出处

     程序的状态处理几乎是每个应用系统都需要做的事情,而且在逻辑控制中处于非常重要的角色,一般来讲,设计得好的系统,都会有一套自己的状态维护机制——状态机,状态机无非就是一个switch case的封装,以及log记录,随着状态的逐渐复杂以及需求的改变,最开始设计的状态机可能会面临面目全非的修改,通过一个简单的配置,可视化的检查,是提高编程效率、降低风险的好办法。SMC就是提供一个配置文件——UML状态图——JAVA(或其他语言)代码生成的软件。

     按照官方的说法,SMC目前支持11种语言的代码自动生成:

SMC currently supports ten programming languages:
  1. C,
  2. C++,
  3. C#,
  4. Java,
  5. Lua,
  6. Objective-C,
  7. Perl,
  8. Python,
  9. Ruby,
10. [incr Tcl] and
11. VB.Net.

     SMC官方网站:http://smc.sourceforge.net/

     下载最新的SMC包:smc_5_0_0.zip,设置环境变量:

+ Add the full path to .../Smc/bin to your PATH environment  variable.
+ Add the full path to statemap.jar to your CLASSPATH environment  variable.
+ Add the full path to .../Smc/lib to your TCLLIBPATH environment  variable.

SmcLogo

      Sample:

1、就拿SMC提供的地铁自动轧机的状态例程作为案例,状态图如下:

Lyon 进

2、在这里我们将状态转变修改了一下,状态变成unLock后,如果调用pass命令,那么根据用户的性别判断是执行doWork1()还是doWork2()动作,并且执行doLock动作。

3、编写SM(?)文件Turnstile.sm——描述状态关系变化的文本文件;

%class Turnstile
%package test.smc

%start MainMap::Locked

%map MainMap
%%
Locked{
    coin UnLocked    {unlock();}
    pass(sex:int) nil            {alarm(sex);}
}

UnLocked{
    pass(sex:int)[ctxt.isBoy(sex)]  Locked        {doWork1();lock();}
    pass(sex:int)[ctxt.isGirl(sex)] Locked        {doWork2();lock();}
    coin nil            {thankyou();}
}

%%

官方的注释(注意:代码跟上面的例程不一样):

Lyon 进

 

 

 

 

 

 

 

 

 

4、将smc_5_0_0.zip包中的smc.jar与Turnstile.sm放在一起,编译:

java -jar Smc.jar -java Turnstile.sm

5、生成TurnstileContext.java,手工编写Turnstile.java:

package test.smc;

import test.smc.TurnstileContext.TurnstileState;

public class Turnstile implements ITurnstileActions {
    TurnstileContext _fsm;
    ITurnstileActions _actions;   

    public Turnstile(ITurnstileActions actions) {
        this._actions = actions;
        this._fsm = new TurnstileContext(this);
    }

    public TurnstileState getCurrentState() {
        return this._fsm.getState();
    }

    public void coin() {
        _fsm.coin();
    }

    public void pass(int sex) {
        _fsm.pass(sex);
    }

    public boolean isBoy(int sex) {
        return sex==0;
    }

    public boolean isGirl(int sex) {
        return sex==1;
    }

    public void alarm(int sex) {
        _actions.alarm(sex);
    }

    public void lock() {
        _actions.lock();
    }

    public void thankyou() {
        _actions.thankyou();
    }

    public void unlock() {
        _actions.unlock();
    }
    public void doWork1(){
        _actions.doWork1();
    }
    public void doWork2(){
        _actions.doWork2();
    }
}

ITurnstileActions.java

package test.smc;

public interface ITurnstileActions {
    public void alarm(int sex);
    public void lock();
    public void thankyou();
    public void unlock();
    public void doWork1();
    public void doWork2();
}

TurnstileActionsImpl.java

package test.smc;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class TurnstileActionsImpl implements ITurnstileActions {
    private static Log logger = LogFactory.getLog(TurnstileActionsImpl.class);
    public void alarm(int sex) {
        logger.info("____________alarm");
    }

    public void lock() {
        logger.info("____________lock");
    }

    public void thankyou() {
        logger.info("____________thankyou");
    }

    public void unlock() {
        logger.info("____________unlock");
    }
    public void doWork1(){
        logger.info("____________Hello boy!");
    }
    public void doWork2(){
        logger.info("____________Hello girl...!");
    }
}

6、编写测试代码:

package test.smc;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import junit.framework.TestCase;

public class TestTurnstile extends TestCase {
    private static Log logger = LogFactory.getLog(TestTurnstile.class);
    private Turnstile turnstile;
    public void setUp(){
        turnstile = new Turnstile(new TurnstileActionsImpl());
    }
    public void testCoin1(){
        System.out.println();
        System.out.println();
        System.out.println("Start testCoin1");
        logger.info("State msg: " + turnstile.getCurrentState().getId() + ": " + turnstile.getCurrentState().getName());
        turnstile.coin();
        System.out.println();
        logger.info("State msg: " + turnstile.getCurrentState().getId() + ": " + turnstile.getCurrentState().getName());
        turnstile.pass(0);
        System.out.println("End testCoin1");
    }
    public void testCoin2(){
        turnstile = new Turnstile(new TurnstileActionsImpl());
        System.out.println();
        System.out.println();
        System.out.println("Start testCoin2");
        turnstile.pass(1);
        turnstile.coin();
        turnstile.coin();
        turnstile.coin();
        turnstile.pass(1);
        System.out.println("End testCoin2");
    }
}

7、执行结果:

Start testCoin1
2007-12-9 0:34:48 test.smc.TestTurnstile testCoin1
信息: State msg: 0: MainMap.Locked
2007-12-9 0:34:49 test.smc.TurnstileActionsImpl unlock
信息: ____________unlock

2007-12-9 0:34:49 test.smc.TestTurnstile testCoin1
信息: State msg: 1: MainMap.UnLocked
2007-12-9 0:34:49 test.smc.TurnstileActionsImpl doWork1
信息: ____________Hello boy!
2007-12-9 0:34:49 test.smc.TurnstileActionsImpl lock
信息: ____________lock
End testCoin1

Start testCoin2
2007-12-9 0:34:49 test.smc.TurnstileActionsImpl alarm
信息: ____________alarm
2007-12-9 0:34:49 test.smc.TurnstileActionsImpl unlock
信息: ____________unlock
2007-12-9 0:34:49 test.smc.TurnstileActionsImpl thankyou
信息: ____________thankyou
2007-12-9 0:34:49 test.smc.TurnstileActionsImpl thankyou
信息: ____________thankyou
2007-12-9 0:34:49 test.smc.TurnstileActionsImpl doWork2
信息: ____________Hello girl...!
2007-12-9 0:34:49 test.smc.TurnstileActionsImpl lock
信息: ____________lock
End testCoin2

8、查看测试结果可知,在未有调用任何动作之前,状态的初始值为:MainMap.Locked,也就是%start MainMap::Locked指定的初始值,执行turnstile.coin()之后,状态变为MainMap.UnLocked,在其中我们没有看到任何状态的处理机制——这些状态变化、记录已经在TurnstileContext.java中解决了。看看没有执行coin方法而直接调用pass的结果——alarm,看来状态的逻辑判断也对了。

分析SM配置文件:

Locked{
    coin UnLocked    {unlock();}
    pass(sex:int) nil            {alarm(sex);}
}

UnLocked{
    pass(sex:int)[ctxt.isBoy(sex)]  Locked        {doWork1();lock();}
    pass(sex:int)[ctxt.isGirl(sex)] Locked        {doWork2();lock();}
    coin nil            {thankyou();}
}

在执行pass命令时,需要传入sex,类型为int,ctxt是当前Turnstile实例的名字,是代码自动生成的,不用修改成其他名字,执行ctxt.isBoy(sex)或isGirl(sex)不需要执行函数入口参数的类型,因为pass、isBoy、isGirl方法的调用关系是:

protected void pass(TurnstileContext context, int sex) {
                Turnstile ctxt = context.getOwner();

                if (ctxt.isBoy(sex)) {

                    ……               

               } else if (ctxt.isGirl(sex)) {

                    ……

                } else {
                    super.pass(context, sex);
                }

                return;
            }

其中,pass函数,在调用时,系统会自动传入TurnsitleContext。

9、SMC的特色在于,可以根据SM文件反向生成UML状态图图片(注意,是图片)。这是需要借助第三方工具——graphviz,软件的官方网站是:

http://www.graphviz.org/

Logo比较有意思:doc-about

下载graphviz-2.16.static.exe。

在SM目录执行命令

java -jar smc.jar -graph -glvel 2 Turnstile.sm

-glvel有3个等级,0、1、2,数字越大,生成的状态图就越详细,在这里我们传入2。生成文件:Turnstile_sm.dot,在graphviz中打开它,执行run,生成的图形如下:

Lyon 进

与预期设计的UML图进行比较,发现基本类似:

Lyon 进

摘录一段关于SMC对象内部关系的图:

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值