Reputation: 45692
What I have:
One-to-many relationship between A and B entities. Each of those entities have a status. There are a finit amount of statuses, about 7-12 for each.
I have to check if the transition (from one status to another) is possible. And make few additional tasks on transition, like store history and make logging.
Don't fall in delusion, status isn't equals to state. It isn't about State Pattern
because my entities haven't any state-based logic.
How does it happen:
Entity arrives as a part of update request, and I compare if old version of entity has the same status as the new version. If not - the status has been changed. Status is just a string or enum.
What I want:
Of course, there are some rules, which impose restrictions on state transition. I want the next:
A-entity
can be moved to Y-state
only if it is in X-state
and all related B-entities
are in Z-state
. A-entity
should always be consistent related to all linked B-entities
. Example: If I move B-entity
to X-state
than related A-entity
should be moved to 'Y-state'.What a problem:
The simples solution is to write a transition-matrix, where matrix[i][j] == 1
if transition is possible and 0
otherwise. But there are some problems:
if-statements
? Question
I'm looking for Pattern/Common Practice suitable for this situation. What is a patter/best practice of solving the task?
Upvotes: 2
Views: 134
Reputation: 9492
Here something simple I wrote based on Enums :) It was originaly written as art of this post.How to implement status transitions for an Entity in java? Its purpose is to express state through Hibernate.
public enum State {
STATE1,STATE2(STATE1),STATE3(STATE1,STATE2);
private State[] previousStates;
private State(){
}
private State(State ...state) {
this.previousStates =state;
}
public State transition(State state) throws Exception {
for(State tmp: state.previousStates) {
if (this == tmp) {
return state;
}
}
throw new Exception("Illegal state");
}
}
Upvotes: 0
Reputation: 10455
The A- and B-items should probably implement the same interface, such as
boolean canMove(State newState); // A-items know which B-items to check
void performMove(State newState, HistoryAndLog historyAndLog); // A-items and B-items will record history and log as appropriate. A-item will change related B-items as appropriate
In this case we are using composite (A-items check if related B-items can change state as desired) and delegation (have A- and B-items update the log and store history as they see fit). This removes the need for a lot of the if-statements.
The underlying design of the A- and B-items should probably be "state pattern", perhaps combined with Flyweight so you can use an underlying transition matrix to implement part of the logic.
EDIT: Expanding on Flyweight... For an N x M transition matrix, storing just a yes/no value takes one bit. If instead you have to store an object pr. cell, that would cost a LOT more (internal representaiton of object). Instead it's cheaper to create a temporary object on request from a factory class. Often by a factor of 100 or more pr. object.
Basically you are trading computation in the situation (create the object on demand) for storage (avoid storing N x M objects).
One could imagine something like this
class Factory {
private boolean canTransitionItemA(final int n, final int m) {
// Look up in matrix
}
public ItemType makeItemAFromCoordinates(final int n, final int m) {
return new ItemA() {
public boolean canTransition(<blabla>) {
return canTransitionItemA(n, m) && canTransitionRelatedBItems(n, m);
}
<bla bla bla>
}
}
}
Upvotes: 1
Reputation: 4391
You are insistent that it's not a state pattern, but your description is exactly that of a finite state machine. You have a finite set of statuses. You have transition events where the status is changed. Those transition events need to have guards to ensure the transition is valid. You have exit and/or entry actions (logging). That's all classic state machine behavior.
Check out http://smc.sourceforge.net/ . It's a project that will take a .sm file you create and generate a state machine for you in your choice of 14 languages.
One reason to use a tool like this is that a code generator produces already working code. You don't have to test the mechanics of the state machine it produces, you just have to test your transitions and validations.
Upvotes: 2