Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

State

4.43/5 (4 votes)
6 Feb 2010CPOL3 min read 11.6K  
State

Imagine that you need to develop an 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 these requirements easily?

STATE

Allowable States

To be more clear about the behavior of our Order, let's take a look at the next statechart diagram:

Image 1

We could encapsulate behavior which belongs to the 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

Image 2

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.

C#
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.

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

If the current state is Granted, it changes Order's state to Shipped and if needed, does 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.

C#
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 care what method ship() is doing in Order class, it could be like shipping :).

C#
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.

C#
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:

C#
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

C#
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.

C#
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:

Image 4

Yes, but this is the way to greatly encapsulate your behavior. Also, there are other ways to resolve the problem. For example, usage of the table [state|method|state] which is populated with all allowable transitions. You could also resolve the 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 senses 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)