thehiflyer / Fettle

A state machine framework for java
http://thehiflyer.github.com/Fettle/
MIT License
24 stars 6 forks source link

Suggestion #2

Closed wwadge closed 11 years ago

wwadge commented 11 years ago

Nice work.

It would be nice if there's a way to expose the state machine links so that you could for e.g. dump out the state for processing by "dot" to produce a graphic image.

thehiflyer commented 11 years ago

Hi,

thanks. There is actually a DotExporter already (https://github.com/thehiflyer/Fettle/blob/master/src/main/java/se/fearless/fettle/export/DotExporter.java) that manages to do this. You are of course free to write your own version or give me suggestions on how to improve the existing one. Are there any methods you are missing?

wwadge commented 11 years ago

Awesome - sorry I missed that!

I started off by looking at stateless4j but the code's a mess while yours is nice and clean. The only thing I can't see an equivalent of is hierarchical states eg in a credit card transaction you could have AUTH with substates being set as AUTH_PENDING, AUTH_RECEIVED etc. Yes they're separate internally but via the API you could ask: "is it is an AUTH state or one of it's substates?" i.e. it's a purely logical api call.

Congrats once again on your codebase and thanks for making this public.

thehiflyer commented 11 years ago

I've thought about it quite a bit but I haven't really been able to see how it could fit in the Fettle model. I guess the best way to do it is to add that kind of semantics to the states themselves. Something like this:


package se.fearless.fettle;

import com.google.common.collect.Lists;
import org.junit.Test;
import se.fearless.fettle.builder.StateMachineBuilder;

import java.util.List;

import static org.junit.Assert.assertTrue;

public class Foo {
    @Test
    public void subStates() {
        StateMachineBuilder<State, String, Void> builder = StateMachineBuilder.create(State.class, String.class);

        builder.transition().from(State.A).to(State.B1).on("ab");
        builder.transition().from(State.B1).to(State.B2).on("b1b2");
        builder.transition().from(State.B2).to(State.B3).on("b2b3");
        builder.transition().from(State.B3).to(State.C).on("bc");

        StateMachine<State, String, Void> stateMachine = builder.build(State.A);
        stateMachine.fireEvent("ab");
        stateMachine.fireEvent("b1b2");
        State currentState = stateMachine.getCurrentState();
        assertTrue(currentState.isIn(State.B));
        assertTrue(currentState.isIn(State.B2));
    }

    private enum State {
        A,
        B,
        B1(B), B2(B), B3(B),
        C;
        private final List<State> superStates;

        State(State... superStates) {
            this.superStates = Lists.newArrayList(superStates);
        }

        public boolean isIn(State state) {
            return this == state || superStates.contains(state);
        }
    }
}
thehiflyer commented 11 years ago

I will close this issue. If you don't think that my solution is good enough and have an idea on how to integrate it into the framework, please open another issue since it's not really related to the original issue.

wwadge commented 11 years ago

Agreed re closing. I'd just change isIn() method to be recursive upwards rather than just .contains() like you'd do in .equals