How should an object be transferred using the state template to the next state?

I have an Order class that goes through a series of defined states. To help with this, I applied the State pattern so that the Order object has a CurrentState member that implements the IOrderState interface. Then I have specific implementations of this interface, such as OrderStateNew, OrderStateDelivered, etc. Etc.

My question is: what is the correct way to transition an Order object between states? Is it permissible to have an Order.SetState () method that allows an external service to set state? The criteria that determine state changes are stored externally on the Order object, so this seems like the obvious answer, but I'm a little awkward that the public method on my object changes something as fundamental as this.

Additional clarification I thought it would be useful to add more detailed information about my implementation, because I am wondering if I use the template correctly in the first place. Here is the pub API for creating and authorizing an order

Dim orderFacade As New OrderFacade Dim order = orderFacade.createFrom(customer) ' Add lines etc ' This will validate the order and transition it to status 'Authorised' Dim valid = orderFacade.Authorise(order) ' This will commit the order, but only if it is at status 'Authorised' Dim result = orderFacade.Commit() 

The OrderFacade.Authorise () function looks something like this:

 Public Function Authorise(ByRef originalOrder As Order) As ValidationSummary If originalOrder.CurrentState.CanAuthorise() Then Dim validator = OrderValidatorFactory.createFrom(originalOrder) Dim valid = validator.ValidateOrder(originalOrder) If valid.IsValid Then originalOrder.SetOrderStatus(OrderStatus.Authorised) End If Return valid End If End Function 

As you can see, the CurrentState member is the current implementation of IOrderState, which determines which actions are valid for the object. I am wondering if this should be responsible for defining the transition, and not in OrderFacade?

+4
source share
5 answers

Consider a state change by implication, and not by designation.

In almost all cases that I have ever seen, the state can be inferred from other properties (I hope, within the framework of the class). If so, do not save the state, but retrieve it when necessary. Otherwise, you will often encounter problematic disagreements between the estimated and assigned values. (And in my experience, "inferred" is always right.)

(The difficulty often comes down to looking at the transaction log for the class and reviewing the most recent events, but thatโ€™s all the same.)

+3
source

The SetState () method would have advantages in expanding orders with additional states, as well as in instrumental changes, but I would not recommend it. A state pattern is a collection of behavior specific to individual states in separate classes, not how to present state-supported interfaces to other classes.

For orders, think about business events that occur naturally (for example, confirmation, confirmation, delivery notification, delivery, invoice, etc.) and create an explicit interface around them. Exactly how you design the interface depends on how your application logic is structured and how it is used from other layers. The classic answer is to define abstract methods for each business event (for example, Confirm (), Acknowledge (), ShipDateChanged ()). If you use, for example, C #, you can accept inbound and outbound events from Order objects. Or you can try a mixture or a combination of them. The main thing is that the SetOrderState () interface is not very descriptive and can lead to a clumsy implementation (large methods in each of the OrderState classes).

Again, the SetState () method internal to your classes (called from each of your specific methods or events) may be OK if you don't have a lot of codes around different state changes, but I would not expose this as an external interface . The disadvantage is that you may get some match between the methods of your internal IOrderState interface and the external interface of the order.

This is a call to judgment, but if I were you, I would go with your instinct not to disclose the details of the implementation of your state to customers. The code that your Order class uses must be readable and understandable.

+2
source

You can reduce the visibility of a method, for example, for a private package. but in your case, I think this is the only way, another way to have a parent abstract class that implements a state machine, and just have a group of nextState (inputParameter) methods that will translate the order state into the corresponding state.

+1
source

I do not think that this requires a โ€œcorrectโ€ answer; it really depends on the architecture you have chosen for your machine. If all the logic of the state change were encapsulated in your Order class, I would say that it would be bad practice to set the SetState method. However, since you have already placed certain state determination logic outside the Order class, it seems appropriate (and necessary) to publish the public SetState method or something similar.

Of course, your other choice would be to move the state determination logic to the Order class, although based on what you posted it seems like it will create a very complex class.

In any case, in short, patterns do exist to help you architect your code, rather than setting hard and fast rules. You should apply templates to your architecture the way it works best, and not archive the templates.

+1
source

I think the transition between states should be in class.

For example, when you perform an action in order, the order knows how to go to another state. For instance:

  public void setPaid(int amount) { //Code to pay. this.State = new PaidState(); //State implements the IState Interface } 

Another alternative would be to create a new class for the Transformer example that implements such a method.

  public class Transformer { public static void setState(Order o, IState s) { //Change the State. } } 

Or yoy can use Enum to set states there:

  public class Transformer { public static void setState(Order o, StatesEnum s) { //Change the State. } } 

But I recommend the class manipulate its own state. But remember the difficulty that you will have on the other hand.

Best wishes!

+1
source

All Articles