This is my first stab at how a state machine might be used. I still not clear on how I would like to define views, so I'm going to leave that out.
Please don't look for any sort of logical consistency in these examples, they are just random ideas to illustrate what I got in mind. Also bear in mind that I'm a spring novice who is still working through the book.
BTW, I've started to refer to commands/actions as operations because those terms are being used elsewhere. This should help add to the confusion :-)
Code:
<beans>
<!--
In an MDI application the singleton attribute would need to
be set to false
-->
<bean id="bookingStateMachine" class="org....StateMachineEngine"
singleton="false">
<!-- An operation to take the machine to the first state -->
<property name="start">
<ref bean="bootOperation"/>
</property>
<!-- Watch for this in the context passed to operations -->
<property name="document">
<bean class="MyDataModel" singleton="false"/>
</property>
</bean>
<!--
Wiring up an operation. The result returned by the execute
method on an operation is mapped on to the id of the next state.
There may be no results map, there does not have to be a
change in state.
Somewhere in here I want to be able to push the current
state onto a stack for a later pop, or even pop it off.
Any ideas?
-->
<bean id="bookSeat" class="org....OperationMapper">
<property name="operation">
<bean class="example.BookSeatOperation"/>
</property>
<property name="results">
<map>
<entry key="next">
<ref bean="myNextState/>
</entry>
<entry key="badData">
<ref bean="displayErrorState/>
</entry>
</map>
</property>
</bean>
<!--
Different states can share sets or subsets of operations.
-->
<bean id="operationSet1" class="org.....OperationSet">
<property name="operations">
<set>
<ref bean="bookSeat"/>
<ref bean="viewAvailability"/>
</set>
</property>
<property name="include">
<set>
<ref bean="operationSubSet1"/>
<ref bean="operationSubSet2"/>
</set>
</property>
</bean>
<bean id="displayingWizardPage4State" class="org.....State">
<property name="operationSet">
<ref bean="operationSet1"/>
</property>
</bean>
.....
Code:
/**
* The interface implemented by the programmer to provide workflow logic.
*
* There may be more than one instance of a document requiring the same set of
* states and operations in an application (e.g. an MDI application).
* So Operation and State beans should not hold references to the current
* document. They should take it from the passed context.
*/
public interface Operation {
/**
* Do some workflow logic and indicate the outcome.
* @return a string indicating the result.
*/
public String execute(Context context);
/**
* Is this operation currently possible? Not called unless in a state
* that allows the action.
*/
public boolean isEnabled(Context context);
}
Code:
/**
* Provided by the state machine to operations.
*/
public interface Context {
public State getState();
/**
* The object provided by the programmer to the state machine.
* It is a means by which operations and GUI views communicate.
*/
public Object getDocument();
/**
* If this is called by the operation then the state machine gives any
* registered GUI the chance to display the progress.
* <P>
* Swing must not drive the state machine using the event dispatch
* thread for this to happen.
*/
public void showProgress(int min, int max);
/**
* Set the amount of progress to display.
*/
public void setProgress(int progress, String messageKey);
}
How we might add a Listener to the state machine....
Code:
<beans>
<bean id="myStateMachine" class="org....StateMachine"
singleton="false">
.....
<property name="listeners">
<list>
<ref bean="myGuiStateManager"/>
</list>
</property>
</bean>
<!--
So that in an MDI application we get a new set of views for
each document, but we re-use them when working within that
document, We give each it's own view factory. Also nice
to read these definitions from another file.
- Does this work?
-->
<bean id="myGuiStateManager" class="org....GuiManager"
singleton="false">
<property name="viewFactory">
<bean "org....XmlViewFactory">
<constructor-arg>
myswingviews.xml
</constructor-arg>
<constructor-arg>
myStateViewMap
</constructor-arg>
</bean>
</property>
</bean>
....
And that's as far as I've got.
I do have ideas about views, but they keep changing. I want to treat views as containers with different panels that are attached depending on state. This has worked in my current app, but I don't think it will suit everyone.
Reg.