Here is an example of a simple state machine, taken from Wiki.
When you think about how to express it in a plain text, it is not an easy task because those relation ship is not one-dimensional or one-directional.
I was able to find several different notations for FSM but I found SCXML most promising.
http://en.wikipedia.org/wiki/SCXML
The UML above can be written in an XML:
< ?xml version="1.0" encoding="UTF-8"?>
< scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="ready">
< state id="ready">
< transition event="watch.start" target="running"/>
< /state>
< state id="running">
< transition event="watch.split" target="paused"/>
< transition event="watch.stop" target="stopped"/>
< /state>
< state id="paused">
< transition event="watch.unsplit" target="running"/>
< transition event="watch.stop" target="stopped"/>
< /state>
< state id="stopped">
< transition event="watch.reset" target="ready"/>
< /state>
< /scxml>
Now I want to translate the state machine into a programing language and I want to force the state at compile time.package stateMachine;public interface Watch_FiniteStateMachine {The set of interface will be used like this:
public StartingState initial();
public StoppedState finalState();
static public interface StartingState {
public RunningState start();
}
static public interface RunningState {
public PausedState pause();
public StoppedState stop();
}
static public interface PausedState {
public RunningState resume();
public StoppedState stop();
}
static public interface StoppedState {
}
}
Watch_FiniteStateMachine watchFSM = new Watch_FiniteStateMachineImpl();This is a simple case but it shows that the state change is determined at compile time. Since there is no way for us to get "PausedState" object until we get "RunningState", the order of state machine is forced at compile time.
StartingState watchStarting = watchFSM.initial();
RunningState watchRunning = watchStarting.start();
PausedState watchPaused = watchRunning.pause();
StoppedState watchStopped = watchPaused.stop();
If we can automatically generate the set of interface from a SCXML, it will be very useful.
In Java, fortunately, there is a compile time "annotation" so that I can execute my own Java program at compile time through java compiler. Most of time the feature is used to map SQL table and Java class member variables. I think it should work for my idea as well.
I have another example of file handler.
The problem of traditional file handle wrapping classes is that it has several methods and they are allowed to be used anytime. For example, even though "open()" method must be called before "read()" or "write()", there is no way for compiler to force it. This problem can be addressed by state machine.
< scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="readyToOpen">
< state id="readyToOpen">
< transition event="openForRead" target="reading"/>
< transition event="openForWrite" target="writing"/>
< transition event="openForReadAndWrite" target="readingAndWriting"/>
< /state>
< state id="reading">
< transition event="close" target="readyToOpen"/>
< /state>
< state id="writing">
< transition event="close" target="readyToOpen"/>
< /state>
< state id="readingAndWriting">
< transition event="close" target="readyToOpen"/>
< /state>
< /scxml>
This SCXML can be (automatically) translated to a set of interface; except the OpenAction part below:
public interface FileHander_FiniteStateMachine {
public ReadyToOpenState initial();
static public interface ReadyToOpenState {
static public interface OpenAction {
public void action(String line);
}
public ReadingState openForRead(String filename, OpenAction action);
public WritingState openForWrite(String filename, OpenAction action);
public ReadingAndWritingState openForReadAndWrite( String filename, OpenAction action);
}
static public interface ReadingState {
public ReadyToOpenState close();
}
static public interface WritingState {
public ReadyToOpenState close();
}
static public interface ReadingAndWritingState {
public ReadyToOpenState close();
}
}
The set of interface can be used like this:
FileHander_FiniteStateMachine fileFSM = new FileHander_FiniteStateMachineImpl();
ReadyToOpenState fileReady = fileFSM.initial();
ReadingState fileReading = fileReady.openForRead("filename.txt",
line -> System.out.println(line));
ReadyToOpenState fileReadyAgain = fileReading.close();
Note that I used lamda expression with a functional interface, "OpenAction", which is recent new feature from JDK-8.
Up to this point, it sounds good but there is a down-side I am reluctant to mention. This type of approach will make the implementation part more complicated; in the case of FileHandler example, the implementation class, "FileHandler_FiniteStateMachineImpl", will be more complicated than traditional file handling classes. It is due to the over-head of wrapping state with new objects. Performance-wise, it wouldn't be much different but the number of source code lines will be increased so does the difficulty of maintainability.
I am hoping that I can come up with better idea of suppressing the complexity over-head with the awesome compile-time annotation feature.
No comments:
Post a Comment