my-statemachine和my-rulemachine一些值得借鉴的代码和思想

1、基本模型

my-statemachine

使用一个工厂(factory)创建一个状态机的构造器(builder),builder包含5个要素:起始状态(from)、目标状态(to)、触发事件(on)、条件判断(when)、执行动作(perform),再使用builder构建一个状态机实例,传入起始状态、触发事件、相关参数(非必要)启动状态机。

public void testExternalNormal() {
        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
        builder.externalTransition()
                .from(States.STATE1)
                .to(States.STATE2)
                .on(Events.EVENT1)
                .when(checkCondition())
                .perform(doAction());

        StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);
        States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context());
        Assert.assertEquals(States.STATE2, target);
    }

 

my-rulemachine

创建Facts,使用Rule构造器(builder)创建,builder包含五个要素:规则名称(name)、规则的描述(description)、规则优先级(priority)、判断条件(when)、执行动作(then),构建完成rule后注册到rules中,创建一个规则引擎实例,传入rules和facts启动规则引擎。

public void testAirCond() {
        Facts facts = new Facts();
        facts.put("temperature", 30);

        Rule airConditioningRule = new RuleBuilder()
                .name("air conditioning rule")
                .when(itIsHot())
                .then(decreaseTemperature())
                .build();
        Rules rules = new Rules();
        rules.register(airConditioningRule);

        RulesEngine rulesEngine = new InferenceRulesEngine();
        rulesEngine.fire(rules, facts);
    }

 

2、构造器的级联赋值属性

my-statemachine

通过在interface中声明方法串联,builder实现这些接口,所有接口返回builder自身:

点评:由于使用了三种类型的构造器,这样可以严格控制每种构造器所需要赋值的参数类型及顺序。

public interface ExternalTransitionBuilder<S, E, C> {
    /**
     * 构建transition的初始状态
     * @param stateId
     * @return
     */
    From<S, E, C> from(S stateId);
}

public interface From<S, E, C> {
    /**
     * 构建transition的目标状态
     * @param stateId
     * @return
     */
    To<S, E, C> to(S stateId);
}

public interface To<S, E, C> {
    /**
     * 构建transition的触发事件
     * @param event
     * @return
     */
    On<S, E, C> on(E event);
}


public interface On<S, E, C> {
    /**
     * 构建transition的触发条件
     * @param condition
     * @return
     */
    When<S, E, C> when(Condition<C> condition);
}

public interface When<S, E, C> {
    /**
     * 构建transition的执行
     * @param action
     */
    void perform(Action<S, E, C> action);
}
public class TransitionBuilderImpl<S, E, C> implements ExternalTransitionBuilder<S, E, C>, InternalTransitionBuilder<S, E, C>,
        From<S, E, C>, On<S, E, C>, To<S, E, C>, When<S, E, C> {

    final Map<S, State<S, E, C>> stateMap;

    private State<S, E, C> source;

    protected State<S, E, C> target;

    private Transition<S, E, C> transition;

    final TransitionType transitionType;

    public TransitionBuilderImpl(Map<S, State<S, E, C>> stateMap, TransitionType transitionType) {
        this.stateMap = stateMap;
        this.transitionType = transitionType;
    }

    @Override
    public From<S, E, C> from(S stateId) {
        source = StateHelper.getState(stateMap, stateId);
        return this;
    }

    @Override
    public To<S, E, C> to(S stateId) {
        target = StateHelper.getState(stateMap, stateId);
        return this;
    }

    @Override
    public To<S, E, C> within(S stateId) {
        source = target = StateHelper.getState(stateMap, stateId);
        return this;
    }

    @Override
    public On<S, E, C> on(E event) {
        transition = source.addTransition(event, target, transitionType);
        return this;
    }

    @Override
    public When<S, E, C> when(Condition<C> condition) {
        transition.setCondition(condition);
        return this;
    }

    @Override
    public void perform(Action<S, E, C> action) {
        transition.setAction(action);
    }
}

 

my-rulemachine

直接声明与参数同名的方法,返回builder自身:

点评:由于参数给了默认值,因此一些非必要参数可以选择不设置,代码简洁。

public class RuleBuilder {

    private String name = Rule.DEFAULT_NAME;
    private String description = Rule.DEFAULT_DESCRIPTION;
    private int priority = Rule.DEFAULT_PRIORITY;

    private Condition condition = Condition.FALSE;
    private final List<Action> actions = new ArrayList<>();

    public RuleBuilder name(String name) {
        this.name = name;
        return this;
    }

    public RuleBuilder description(String description) {
        this.description = description;
        return this;
    }

    public RuleBuilder priority(int priority) {
        this.priority = priority;
        return this;
    }

    public RuleBuilder when(Condition condition) {
        this.condition = condition;
        return this;
    }

    public RuleBuilder then(Action action) {
        this.actions.add(action);
        return this;
    }

    public Rule build() {
        return new DefaultRule(name , description, priority, condition, actions);
    }
}

 

3、使用函数式接口将条件判断和执行方法作为入参赋予builder

两者都利用了函数式接口

函数式接口:当接口只有一个抽象方法的时候(可以有其它默认方法),就是函数式接口,可以使用注解(@FunctionalInterface)强制限定接口只能有一个抽象方法。

my-statemachine

可以使用lamda表达式,适合简单的方法,实现起来更简洁。

public interface Condition<C> {
    /**
     * 用户实现的条件判断
     * @param context
     * @return
     */
    boolean isSatisfied(C context);
}

public interface Action<S, E, C> {
    /**
     * 用户实现的状态转移成功时的动作
     */
    void execute(S from, S to, E event, C context);
}
    private Condition<Context> checkCondition() {
        return (ctx) -> {
            return true;
        };
    }

    private Action<States, Events, Context> doAction() {
        return (from, to, event, ctx) -> {
            System.out.println(ctx.operator + " is operating " + ctx.entityId + " from:" + from + " to:" + to + " on:" + event);
        };
    }

 

my-rulemachine

可以使用实现类,new一个对象执行对象下的方法,适合复杂的方法

@FunctionalInterface
public interface Condition {

    boolean evaluate(Facts facts);

    /**
     * 无论什么入参一律输出false
     */
    Condition FALSE = facts -> false;

    /**
     * 无论什么入参一律输出true
     */
    Condition TRUE = facts -> true;
}

@FunctionalInterface
public interface Action {

    void execute(Facts facts) throws Exception;
}
public class HighTemperatureCondition implements Condition {

    static HighTemperatureCondition itIsHot() {
        return new HighTemperatureCondition();
    }

    @Override
    public boolean evaluate(Facts facts) {
        Integer temperature = facts.get("temperature");
        return temperature > 25;
    }

}

public class DecreaseTemperatureAction implements Action {

    static DecreaseTemperatureAction decreaseTemperature() {
        return new DecreaseTemperatureAction();
    }

    @Override
    public void execute(Facts facts) {
        System.out.println("It is hot! cooling air..");
        Integer temperature = facts.get("temperature");
        facts.put("temperature", temperature - 1);
    }
}

 

4、使用工厂而不是直接new一个builder

my-statemachine

在factory中声明一个ConcurrnetHashMap,用来存储machineId及对应的状态机,可以对状态机进行复用。

特别适合多状态转移的情况使用

public void testExternalInternalNormal() {
        StateMachine<States, Events, Context> stateMachine = buildStateMachine("testExternalInternalNormal");

        Context context = new Context();
        States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, context);
        Assert.assertEquals(States.STATE2, target);
        target = stateMachine.fireEvent(States.STATE2, Events.INTERNAL_EVENT, context);
        Assert.assertEquals(States.STATE2, target);
        target = stateMachine.fireEvent(States.STATE2, Events.EVENT2, context);
        Assert.assertEquals(States.STATE1, target);
        target = stateMachine.fireEvent(States.STATE1, Events.EVENT3, context);
        Assert.assertEquals(States.STATE3, target);
    }

 

5、有层次的对象

my-statemachine

状态机拥有多个状态,每个状态又可以有多个转移对象。

(状态机)stateMachine->(状态对象集合)stateMap->(状态转移对象集合)transitions->source、target、event、condition、action、type

 

6、基于注解的Rule参数配置

my-rulemachine

这是一个例子,定义一个Rule

@Rule
public class FizzRule {

    @Condition
    public boolean isFizz(@Fact("number") Integer number) {
        return number % 5 == 0;
    }

    @Action
    public void printFizz() {
        System.out.print("fizz");
    }

    @Priority
    public int getPriority() {
        return 1;
    }
}

声明一个RuleProxy implements InvocationHandler, 在将rule register到rules的时候,直接利用动态代理,将FizzRule转换成一个Rule Class,并代理Rule Class下的全部接口。

public class RuleProxy implements InvocationHandler {

...

public static Rule asRule(final Object rule) {
        Rule result;
        if (rule instanceof Rule) {
            result = (Rule) rule;
        } else {
            ruleDefinitionValidator.validateRuleDefinition(rule);
            result = (Rule) Proxy.newProxyInstance(
                    Rule.class.getClassLoader(),
                    new Class[]{Rule.class, Comparable.class},
                    new RuleProxy(rule));
        }
        return result;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        switch (methodName) {
            case "getName":
                return getRuleName();
            case "getDescription":
                return getRuleDescription();
            case "getPriority":
                return getRulePriority();
            case "compareTo":
                return compareToMethod(args);
            case "evaluate":
                return evaluateMethod(args);
            case "execute":
                return executeMethod(args);
            case "equals":
                return equalsMethod(args);
            case "hashCode":
                return hashCodeMethod();
            case "toString":
                return toStringMethod();
            default:
                return null;
        }
    }

...
}

通过原始类获取其上的注解,来实现注解参数的解析

    static <A extends Annotation> A findAnnotation(final Class<A> targetAnnotation, final Class<?> annotatedType) {
        A foundAnnotation = annotatedType.getAnnotation(targetAnnotation);
        if (null == foundAnnotation) {
            for (Annotation annotation : annotatedType.getAnnotations()) {
                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (annotatedType.isAnnotationPresent(targetAnnotation)) {
                    foundAnnotation = annotationType.getAnnotation(targetAnnotation);
                    break;
                }
            }
        }
        return foundAnnotation;
    }

代理执行方法时,通过获取原始Class下的所有Method并判断其注解,然后通过invoke调用。

    private Method getConditionMethod() {
        if (this.conditionMethod == null) {
            Method[] methods = getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Condition.class)) {
                    this.conditionMethod = method;
                    return this.conditionMethod;
                }
            }
        }
        return this.conditionMethod;
    }

    private List<Object> getActualParameters(Method method, Facts facts) {
        List<Object> actualParameters = new ArrayList<>();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (Annotation[] annotations : parameterAnnotations) {
            if (1 == annotations.length) {
                String factName = ((Fact) (annotations[0])).value();
                Object fact = facts.get(factName);
                if (fact == null && !facts.asMap().containsKey(factName)) {
                    throw new NoSuchFactException(format("No fact named '%s' found in known facts: %n%s", factName, facts), factName);
                }
                actualParameters.add(fact);
            } else {
                actualParameters.add(facts); //validated upfront, there may be only one parameter not annotated and which is of type Facts.class
            }
        }
        return actualParameters;
    }

    private Object evaluateMethod(final Object[] args) throws IllegalAccessException, InvocationTargetException {
        Facts facts = (Facts) args[0];
        Method conditionMethod = getConditionMethod();
        try {
            List<Object> actualParameters = getActualParameters(conditionMethod, facts);
            return conditionMethod.invoke(target, actualParameters.toArray()); // validated upfront
        } catch (NoSuchFactException e) {
            log.warn("Rule '{}' has been evaluated to false due to a declared but missing fact '{}' in {}",
                    getTargetClass().getName(), e.getMissingFact(), facts);
            return false;
        } catch (IllegalArgumentException e) {
            log.warn("Types of injected facts in method '{}' in rule '{}' do not match parameters types",
                    conditionMethod.getName(), getTargetClass().getName(), e);
            return false;
        }
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值