Imagine that you need to develop application for shipping Orders. Your Orders could be in few states: New Order, Registered, Granted, Shipped, Invoiced, Cancelled.
And there are some rules which allow your Order to migrate to another state. This means that for example you cannot ship not registered Order.
Also there are some behavioral rules. For example you cannot add new Product to your Order when it is in Cancelled state. Also behavioral rules could mean changes in state.

How could you accomplish this requirements easily?

STATE
Allowable states
To be more clear about the behavior of our Order, lets take a look on the next statechart diagram:

We could encapsulate behavior which belongs to current object’s state into separate classes derived from some parent class. Each of the concrete will be responsible for allowing changing state from one to another.

State Pattern UML

State Pattern UML in application to Order problem

Order State

How does it work?

At first our Order class has a reference to its _state, to have more real example Order also has _products.

public class Order {

private OrderState _state;
private List<Product> _products = new ArrayList<Product>();

public void setOrderState(OrderState state){
     _state = state;
}

Order delegates some state-specific requests to the current state.

public void ship(){
    _state.ship();
}

 – If the current state is Granted, it changes Order‘s state to Shipped and if needed do some surrounded work or delegates it.

This code represents concrete implementation of the OrderState. Constructor of base class has Order parameter, so this allows us have reference to holding Order: _order in derived classes.

public class Granted extends OrderState{

    public Granted(Order holdingOrder) {
        super(holdingOrder);
    }

    public void ship(){
        _order.doShipping();
        //next line changes Order’s state
        _order.setOrderState(new Shipped(_order));
    }

If you are care what method ship() is doing in Order class it could be like shipping :).

public void doShipping(){
    System.out.println(“Shipping…”);
}

 – If current state is Registered, most likely that class has no implementation of method ship(), it has only addProduct(), grant(), and cancel(). So method of super class will be called. OrderState has all bulk of methods but they all throw exceptions, which says that changing state in current situation is not allowed.

public class OrderState {

    protected Order _order;

    public OrderState(Order holdingOrder){
        _order = holdingOrder;
    }
   
    public void ship(){
        raiseNotAllowableOperation(“ship”);
    }
    
    //other methods like register(), grant(), etc… here.. 
    //they look like the ship() looks

   
    private void raiseNotAllowableOperation(String operation) throws IllegalStateException {
        String stateName = this.getClass().getName();
        System.out.println(“ERROR: Operation [“+operation+“] is not allowed for current order state: “ + stateName);
        //of course in real system you will probably throw exception
        //throw new IllegalStateException(“This operation is not allowed for current order state.”);
    }

Example of Usage of Order class

Now we navigating to customer code. Our OrderingSystem creates new Order, adds some beer as product and all in correct way:

public void doHardWork(){
    Product beer = new Product();
    beer.Name = “Lvivske”;
    beer.Price = 78000;
   
    Order order = new Order();
    order.printStateName();
    order.addProduct(beer);
    order.printStateName();
   
    order.register();  
    order.printStateName();
   
    order.grant(); 
    order.printStateName();
   
    order.ship();  
    order.printStateName();
   
    order.invoice();   
    order.printStateName();
}

Output:

Order state: designpatterns.state.NewOrder
Product addeding calculation…
Order state: designpatterns.state.NewOrder
Registration…
Order state: designpatterns.state.Registered
Granting…
Order state: designpatterns.state.Granted
Shipping…
Order state: designpatterns.state.Shipped
Invoicing…
Order state: designpatterns.state.Invoiced

Ok, now we added code, which tries to add more beer when order has been already shipped.

    order.ship();  
    order.printStateName();
   
    //trying to add more beer to already shipped order
    order.addProduct(beer);
    order.printStateName();

Output:

Order state: designpatterns.state.NewOrder
Product addeding calculation…
Order state: designpatterns.state.NewOrder
Registration…
Order state: designpatterns.state.Registered
Granting…
Order state: designpatterns.state.Granted
Shipping…
Order state: designpatterns.state.Shipped
ERROR: Operation [addProduct] is not allowed for current order state: designpatterns.state.Shipped

Other ways to solve problem without State Pattern

One of the disadvantages of this you could see many of the classes needed to have:

Yes, but this is the way to greatly encapsulate your behavior. Also there are other ways to resolve problem. For example usage of the table [state|method|state] which is populated with all allowable transitions. You could also resolve issue with having switch-case method. About all of this you could read in Jimmy Nilsson’s book “Applying Domain-Driven Design and Patterns“.

What have I learned?

  • Java has no “virtual” keyword, that is because all methods are virtual. Make sense, cause in C# it is often needed to write huge amount of that word.
  • I discovered few Exceptions that Java has, for example IllegalStateException.
  • I was not able to easily figure out what to use instead of “:” in declaration of derived classed. Now I know “extends“.
  • I improved my UML skills with practicing drawing them.

I hope you enjoyed this story.

Go to: My Design Patterns Table