Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should an object using the state pattern be transitioned to the next state?

I have an Order class which goes through a series of defined states. To aid with this, I have implemented the State pattern such that the Order object has a CurrentState member which implements an IOrderState interface. I then have concrete implementations of this interface such as OrderStateNew, OrderStateDelivered etc etc

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

Additional clarification I thought it might be useful to add some more detail about my implementation because I wonder if I'm actually using the pattern correctly in the first place. Here's the pulbic API for creating and authorising 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 IOrderState implementation which determines which activities are valid for the object. I wonder whether this should be responsible for determining the transition rather than the OrderFacade?

like image 277
James Avatar asked Mar 01 '23 20:03

James


1 Answers

Consider changing the state by implication rather than assignment.

In almost all cases I've ever seen, the State can be inferred from other properties (hopefully within the Class). If so, don't persist the state, but derive it when needed. Otherwise you'll often end up with problematic disagreements between inferred and assigned values. (And in my experience "derived" is invariably right.)

(The complexity often is to review the transaction log for the class, and consider the most recent event(s). But it's worth it anyway.)

like image 72
dkretz Avatar answered Mar 03 '23 10:03

dkretz