Skip to content

Commit

Permalink
Clone the state map so that the StateMachineBuilder can be reused
Browse files Browse the repository at this point in the history
  • Loading branch information
luozhenyu committed Feb 19, 2023
1 parent 5b50a54 commit 54fe065
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public interface Transition<S, E, C>{

void setEvent(E event);

TransitionType getType();

void setType(TransitionType type);
/**
* Gets the target state of this transition.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public class StateMachineBuilderImpl<S, E, C> implements StateMachineBuilder<S,
* StateMap is the same with stateMachine, as the core of state machine is holding reference to states.
*/
private final Map<S, State<S, E, C>> stateMap = new ConcurrentHashMap<>();
private final StateMachineImpl<S, E, C> stateMachine = new StateMachineImpl<>(stateMap);
private FailCallback<S, E, C> failCallback = new NumbFailCallback<>();

@Override
Expand All @@ -46,6 +45,7 @@ public void setFailCallback(FailCallback<S, E, C> callback) {

@Override
public StateMachine<S, E, C> build(String machineId) {
StateMachineImpl<S, E, C> stateMachine = new StateMachineImpl<>(stateMap);
stateMachine.setMachineId(machineId);
stateMachine.setReady(true);
stateMachine.setFailCallback(failCallback);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.alibaba.cola.statemachine.impl;

import com.alibaba.cola.statemachine.State;
import com.alibaba.cola.statemachine.Transition;

import java.util.HashMap;
import java.util.Map;

/**
Expand All @@ -11,12 +13,26 @@
* @date 2020-02-08 4:23 PM
*/
public class StateHelper {
public static <S, E, C> State<S, E, C> getState(Map<S, State<S, E, C>> stateMap, S stateId){
State<S, E, C> state = stateMap.get(stateId);
if (state == null) {
state = new StateImpl<>(stateId);
stateMap.put(stateId, state);
}
return state;

private StateHelper() {
}

public static <S, E, C> State<S, E, C> getState(Map<S, State<S, E, C>> stateMap, S stateId) {
return stateMap.computeIfAbsent(stateId, StateImpl::new);
}

static <S, E, C> Map<S, State<S, E, C>> cloneStateMap(Map<S, State<S, E, C>> stateMap) {
Map<S, State<S, E, C>> cloneStateMap = new HashMap<>();
stateMap.forEach((stateId, state) -> {
State<S, E, C> cloneState = getState(cloneStateMap, stateId);
state.getAllTransitions().forEach(transition -> {
State<S, E, C> target = transition.getTarget();
Transition<S, E, C> cloneTransition = cloneState.addTransition(transition.getEvent(),
getState(cloneStateMap, target.getId()), transition.getType());
cloneTransition.setCondition(transition.getCondition());
cloneTransition.setAction(transition.getAction());
});
});
return cloneStateMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class StateMachineImpl<S, E, C> implements StateMachine<S, E, C> {
private FailCallback<S, E, C> failCallback;

public StateMachineImpl(Map<S, State<S, E, C>> stateMap) {
this.stateMap = stateMap;
this.stateMap = StateHelper.cloneStateMap(stateMap);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public void setEvent(E event) {
this.event = event;
}

@Override
public TransitionType getType() {
return type;
}

@Override
public void setType(TransitionType type) {
this.type = type;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.alibaba.cola.test;

import com.alibaba.cola.statemachine.Action;
import com.alibaba.cola.statemachine.StateMachine;
import com.alibaba.cola.statemachine.builder.StateMachineBuilder;
import com.alibaba.cola.statemachine.builder.StateMachineBuilderFactory;
import org.junit.Assert;
import org.junit.Test;

/**
* StateMachineBuilderTest
*
* @author luozhenyu
* @date 2023-02-20 01:37 AM
*/
public class StateMachineBuilderTest {

enum States {
STATE1,
STATE2,
STATE3
}

enum Events {
EVENT1,
EVENT2
}

static class Context {
}

@Test
public void testReuseBuilder() {
StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
builder.externalTransition()
.from(States.STATE1)
.to(States.STATE2)
.on(Events.EVENT1)
.perform(doAction());
StateMachine<States, Events, Context> stateMachine1 = builder.build("TestReuseBuilder1");
{
States target = stateMachine1.fireEvent(States.STATE1, Events.EVENT1, new Context());
Assert.assertEquals(States.STATE2, target);
target = stateMachine1.fireEvent(States.STATE1, Events.EVENT2, new Context());
Assert.assertEquals(States.STATE1, target);
}

builder.externalTransition()
.from(States.STATE1)
.to(States.STATE3)
.on(Events.EVENT2)
.perform(doAction());
StateMachine<States, Events, Context> stateMachine2 = builder.build("TestReuseBuilder2");
{
States target = stateMachine1.fireEvent(States.STATE1, Events.EVENT1, new Context());
Assert.assertEquals(States.STATE2, target);
target = stateMachine1.fireEvent(States.STATE1, Events.EVENT2, new Context());
Assert.assertEquals(States.STATE1, target);

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

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

0 comments on commit 54fe065

Please sign in to comment.