编程笔记

如何定义和实现流程

2019-03-03  本文已影响27人  老瓦在霸都

很多程序员在上大学的时候都学过如何画流程图, 或根据流程写代码, 或者根据代码写流程图.

来让我们看看如下的流程, 我们用 www.plantuml.com 所提供的工具绘制如下语音交互应答, 所对应的场景是打电话查询帐户余额

@startuml
start
# 播放欢迎音乐
:play_prompt({"clip":"welcome_with_music"});
:isDone = false;

repeat
# 收集用户的帐号
:idValue = collect_dtmf({"clip":"please_input_id"});
:isRight = check_id(idValue);


if (isRight?) then (yes)
  #如果帐号正确, 取得帐户余额并读出来
  :cash = account_balance(idValue);
  :say(cash);
  :isDone = true;
else (no)
   #如果帐号不正确,  播放错误提示并重新收集帐号
  :play_prompt({"clip":"incorrect_inputted_id"});
  :play_prompt({"clip":"please_input_id"});
endif

:i = i + 1;

repeat while (i < 3 or isDone?)

:play_prompt({"clip":"good_bye"});
stop
@enduml
IVR_Flow

我们可以用代码很容易来实现上述流程, 可是银行的呼叫系统有很多流程, 不同的银行也有很多不同之处, 所以一般我们会预先定义一个工作流


image.png

对于刚才的流程我们需要进行一些抽象, 总结起来就是业务流程模型和标记 BPMN(Business Process Model and Notation), 它是一个业务流程模型及其处理的图形化表示.

sample

BPMN 2.0 规范定义了如下概念

事件

event

事件可以由外部传入, 也可由异常或者超时来触发

活动

活动就是一个个执行单元

activity

网关

网关用户控制流程走向, 也称为执行令牌


gateway

连接

connection

工作流 Workflow 的 BPMN 有不少的实现, 例如著名的 Activiti, 在现实生活中大多数场景并不需要这么重的东西.

比如比较简单的流程可以用职责链模式结合命令模式来实现, 这里用到了 commons chain, 参见 命令和职责链模式的快速实现 commons chain

CoR

比如

package com.cisco.yafan.demo;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;

import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.impl.ContextBase;
import org.junit.Test;

import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertFalse;

@Slf4j
class IvrCommand implements Command {

    private String name;

    public IvrCommand(String name) {
        this.name = name;
    }

    @Override
    public boolean execute(Context context) throws Exception {
        log.info("execute: {} for {}", name, context.get("phoneNumber"));
        if("please input your password".equals(name)) {
            if("pass".equals(context.get("password"))) {
                context.put("balance", 10000);
            } else {
                log.info("your password is incorrect.");
                return true;
            }


        }

        return false;
    }
}

class IvrFlow extends ChainBase {

    public IvrFlow() {

        addCommand(new IvrCommand("welcome"));

        addCommand(new IvrCommand("please input account number"));

        addCommand(new IvrCommand("please input your password"));

        addCommand(new IvrCommand("your account balance is 10000"));

        addCommand(new IvrCommand("goodbye"));

    }
}

@Slf4j
public class CommonsChainTest {
    @Test
    public void testIvrFlowCorrectPassword() throws Exception {
        Context context = new ContextBase();
        context.put("phoneNumber" , "86-123456789");
        context.put("password" , "pass");

        IvrFlow ivrFlow = new IvrFlow();
        ivrFlow.execute(context);
        assertTrue(Integer.valueOf("10000").equals(context.get("balance")));
    }
    @Test
    public void testIvrFlowIncorrectPassword() throws Exception {
        Context context = new ContextBase();
        context.put("phoneNumber" , "86-123456789");
        context.put("password" , "abcd");

        IvrFlow ivrFlow = new IvrFlow();
        ivrFlow.execute(context);
        assertFalse(Integer.valueOf("10000").equals(context.get("balance")));
    }
}

结果输出如下

#testIvrFlowCorrectPassword
execute: welcome for 86-123456789
execute: please input account number for 86-123456789
execute: please input your password for 86-123456789
execute: your account balance is 10000 for 86-123456789
execute: goodbye for 86-123456789

#testIvrFlowIncorrectPassword
execute: welcome for 86-123456789
execute: please input account number for 86-123456789
execute: please input your password for 86-123456789
your password is incorrect.

很显然,这只能应付简单的流程。
稍加扩展, 就可以描绘更复杂的流程。
就象我们在绘制流程图时,除了顺序结构,就是分支结构和循环结构。

分支结构就是:条件判断与执行体
循环结构也是:条件判断与执行体,只不过这个条件包含了何时终止循环

所以我们可以构造一个链表

每个 command 是一个节点,它有一个属性 nextComands 来存储接下来要执行的结点

map<Condition, Command > nextCommands

而 Condition 类包括 variable, value ,这样就可以描述一个复杂的流程了。

class Condition
{
private String variable;
private Object value ;

public boolean test(Context context ) {
    return value.equals(context.get(variable ) ) ;
}
}
上一篇下一篇

猜你喜欢

热点阅读