Writing this blog I found out that the subject of state machine with annotations was already covered in:
I will talk about my experience and personal design, but mainly add the point on why "abstract enum" will help a lot.
state machine and code
First of all, since I wrote AADA I'm convince that UML helps you visualize and conceptualize your code and that's were it should stop. Trying to extract to annotate the UML meta-model so that some nice Java code will be generated, is a proof that the target language and framework are missing features.
For all static UML diagrams, it takes less energy to write a Java class in modern IDE than using any UML tool. But for dynamic diagram and especially '''State Diagrams''', UML beats Java (and big time if you use the good GOF pattern).
Most modern Java framework are solving this issue with XML files:- In Spring Web Flow the flow definition file is an XML that points to fields and methods of your java code (like everything in Spring)
- All vendor workflow
- JBoss JBPM
- Struts, JSF, ...
So with JDK 5, I tried in 2005 to write an event driven state machine with annotations. In general I really prefer to design by events are they are a lot easier to grasp for everyone (business analysist, designers and developers).
So my design is basically based on the fact that each event is typed, and activate a specific object in the model. The '''state machine''' design question was: ''Which method should be activated?''
It is very similar to the previous blogs on the subject.abstract enum will save all
One of the comment on Gabriele Carcassi blog was: "Actually, BeanFlow uses strings (due to depending on enum fields rather than interfaces) to declare transitions. That's not refactorable. I like this design better." So, the conflict between generic framework (using string) and clean design (using annotations and enum), appeared immediatly.
So, I tried to write with my design the Seminar State Diagram from Agile Modeling, and it looks really good:
public class Seminar {
private SeminarState state;
private int maxStudents = 10;
private int nbStudents = 0;
private int nbWaitingStudents = 0;
@ActifFromState({null})
public void create() {
state = SeminarState.proposed;
}
@ActifFromState({/*all states*/})
public void cancel(CancelEvent e) {
maxStudents = 10;
nbStudents = 0;
nbWaitingStudents = 0;
state = null;
}
@ActifFromState({SeminarState.proposed})
public void schedule() {
state = SeminarState.scheduled;
}
@ActifFromState({SeminarState.scheduled})
public void open() {
state = SeminarState.openForEnrollement;
}
@ActifFromState({SeminarState.openForEnrollement})
public void add(NewStudentEvent e) {
nbStudents++;
if (nbStudents >= maxStudents) {
state = SeminarState.full;
}
}
@ActifFromState({SeminarState.full})
public void addWaiting(NewStudentEvent e) {
nbWaitingStudents++;
}
@ActifFromState({SeminarState.openForEnrollement})
public void remove(DropStudentEvent e) {
nbStudents--;
}
@ActifFromState({SeminarState.full})
public void removeWaiting(DropStudentEvent e) {
nbWaitingStudents--;
if (nbWaitingStudents == 0) {
state = SeminarState.openForEnrollement;
}
}
@ActifFromState({SeminarState.full, SeminarState.openForEnrollement})
public void close() {
state = SeminarState.closedForEnrolement;
}
public SeminarState getState() {
return state;
}
}
It looks like I can express the state in a more concise way than UML. Of course UML is still graphically more powerful.
The only issue above is that I need the enum SeminarState to extends abstract enum StateDefinitionpublic abstract enum StateDefinition {
public Class getModelClass() {...};
public StateDefinition getSuperState() {...};
// And many more needed by the framework
}
public enum SeminarState implements StateDefinition {
proposed, scheduled, openForEnrollement, full, closedForEnrolement;
}
and define in the State manager framework an annotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ActifFromState {
StateDefinition[] value();
}
Conclusion
It seems to me like most of the XML coding (writing java code in XML) issues, we are seeing today, can be removed if Annotations can have an abstract enum attribute.
I like it...
2 comments:
Do you have a simple working example that I can download and compile using your abstract enum design?
For abstract enum, the code is in http://kijaro.dev.java.net/ for the state machine, I migrated an annotation based state machine to abstract enum when I did this blog, but it was customer code :(
I really need to get back to it!
Post a Comment