patternjavaMinor
State Transition Table
Viewed 0 times
tabletransitionstate
Problem
I wrote a transition table which allows the client to:
A
After creating nodes, the client is able to run a loop, grabbing the current state from the table and processing it by calling
The application type may be different, so each state needs to be created for that specific application, which can be done using the generic type parameter I gave to
From my perspective, this is highly scalable, compared to
This is the the framework they will be building upon
```
public final class TransitionTable> {
private Map nodes = new HashMap<>();
private StateNode currentNode, startNode;
public StateNode createNode(T state) {
StateNode node = new StateNode(state);
nodes.put(state, node);
- Create
States
- Create a
TransitionTableor some kind ofStateManager
- Create
StateNodesusing that table
A
StateNode is what I use to link one state to other states. It has the mainState and an ArrayList full of states it can jump to.After creating nodes, the client is able to run a loop, grabbing the current state from the table and processing it by calling
process, passing in:- The application
- The table, to make transitions
The application type may be different, so each state needs to be created for that specific application, which can be done using the generic type parameter I gave to
State. The TransitionTable is then given the type of states it will be managing, using the generic type parameter I gave it.From my perspective, this is highly scalable, compared to
enum state systems. The client can plug-in states, and different state types can be extended upon for organization. I have yet to get into annotation processing, so that's why clients must use marker interfaces for cleaner code.This is the the framework they will be building upon
Applicationpublic abstract class Application {
private AtomicBoolean running = new AtomicBoolean(false);
private Thread thread;
public final void start() {
if(running != null && running.compareAndSet(false, true)) {
init();
thread = new Thread(logic);
thread.start();
}
}
private Runnable logic = () -> {
while(running.get()) {
process();
}
};
protected abstract void init();
protected abstract void process();
}TransitionTable with StateNode nested inside:```
public final class TransitionTable> {
private Map nodes = new HashMap<>();
private StateNode currentNode, startNode;
public StateNode createNode(T state) {
StateNode node = new StateNode(state);
nodes.put(state, node);
Solution
Typically, in a GoF State pattern implementation, the StateContext (
Is this really all SPI, and your library is making the calls into
It's also not really clear what the scope of these state machines is going to be. If clients will have 5 or 6 states, that's a very different problem to solve than 500 or 600 states. You said you wanted scalable - to what size?
Anyway, the reason you're having issue #1 is because your library insists on knowing the state transitions, even though your only value-add is checking that a requested transition was defined for the state. If you insist on managing the transitions, I don't think there's a nice way to get around #1.
Based on what I've seen, and with no knowledge whatsoever of your business requirements, my suggestion would be to leave it up to the client to handle the state transitions. You're right that you can't use an
Some thoughts:
TransitionTable in this case) doesn't track the transitions. The states are responsible for handling the transition logic and notifying the context. Why are you taking that burden onto your library? I'm missing the design goal here, and that makes it hard to critique your technical decisions. Is this really all SPI, and your library is making the calls into
TransitionTable? Even in that case, I'm not sure why you need to manage the transitions yourself, except to try to force the client to correctly implement a state machine. Either your clients understand a state machine and that's just an annoying hoop, or they don't and it won't help them very much at all.It's also not really clear what the scope of these state machines is going to be. If clients will have 5 or 6 states, that's a very different problem to solve than 500 or 600 states. You said you wanted scalable - to what size?
Anyway, the reason you're having issue #1 is because your library insists on knowing the state transitions, even though your only value-add is checking that a requested transition was defined for the state. If you insist on managing the transitions, I don't think there's a nice way to get around #1.
Based on what I've seen, and with no knowledge whatsoever of your business requirements, my suggestion would be to leave it up to the client to handle the state transitions. You're right that you can't use an
enum, but they sure can. Your absolute best bet is to take this, stick it in front of a dev from each of your three favorite clients, and ask them to write a representative implementation for their needs. Believe me, you'll find out where your weaknesses are. Some thoughts:
Application#start() - You don't need the null check running != null unless you've forgotten to include running = null when the thread ends.State - Strongly consider adding a getTransitions() method that returns the available transitions. This will fix the cumbersome table.addNode().addTransitions(), and let you hide the existence of StateNode, which clients have no business knowing about. This will also fix the need for clients to remember which transitions were configured, since now they're defined in the same State implementation that uses them. In this case, clients would need to use an enum, or some container of states that all states have access to, or write stateless states so they can instantiate instances as needed. TransitionTable#createNode() - Do clients really need to know about nodes? Why not addState()?DemoApplication#init() - WalkToWork should be WalkingToWork, Sleep should be Sleeping, etc. Only relevant if you intend to release this demo code.DemoApplication#process() - I'd wager that most clients will want exactly this, and I think those that don't are wrong. That should be part of your library.Context
StackExchange Code Review Q#75297, answer score: 5
Revisions (0)
No revisions yet.