This is the 22nd day of my participation in the August More Text Challenge


1. Status determines behavior

Online shopping is very common now, pick up the phone to choose the goods, input the payment code, the buyer can sit and wait for the goods, it is very convenient. Each shopping record of the user corresponds to an order, which has many states. Different states can carry out different operations. For example: the newly created order, the buyer has only two options: either choose to pay or cancel the order, you can not confirm the receipt of the goods, otherwise it will be a mess. Today we’ll use the order state example to understand “state patterns.”

Assume that the order exists in the following four states:

  1. To be paid.
  2. Momentum.
  3. For the goods.
  4. Order completion is complete.

There are four operations for an order:

  1. Payment amount.
  2. Cancel the order.
  3. The seller delivers the goods.
  4. Buyer confirms receipt of goods.

Try to describe this process in code. The class diagram is designed as follows, which is very simple:Start by defining an order status enumerationStateEnum:

enum StateEnum {
	WAIT_PAY,/ / to pay
	WAIT_SEND,/ / momentum
	WAIT_RECEIVE,// Waiting for delivery
	FINISHED;// End of order
}
Copy the code

Core Order class Order:

class Order {
    StateEnum state = StateEnum.WAIT_PAY;// The current order status is to be paid by default

    void pay(a){
        if (state == StateEnum.WAIT_PAY) {
            print("Order payment.");
            state = StateEnum.WAIT_SEND;
        }elseThe current status does not support this operation}}void cancel(a){
        if (state == StateEnum.WAIT_PAY) {
            print("Cancel order.");
            state = StateEnum.FINISHED;
        }elseThe current status does not support this operation}}// omit other methods......
}
Copy the code

The client calls like this:

main() {
    Order order = newOrder(); order.pay(); order.send(); order.receive(); order.cancel(); } Output: order payment. Seller makes delivery. Buyer confirms receipt. Exception: The current status does not support this operation.Copy the code

OK, the function is normal. For the finished order, if the user wants to cancel the order, it will throw an exception and prompt the user “the current status does not support this operation”.

But have you noticed that the Order class is very complex, and there are a lot of if judgments. For all operations of the Order class, the client must first judge whether the current operation is allowed by the Order state. What if another state is extended later? The Order class needs a lot of modification, which seriously violates the “open closed principle”. I believe that in a short time, the Order class will become bloated and unmaintainable.

We tried to optimize the program with “state patterns”. Since the state of the order class determines its behavior, can we entity each state of the order into a class? The operation of the order is delegated to state classes, each of which is responsible for only the operations it can handle. The optimized class diagram design is as follows:The order state enumeration is unchanged, so let’s first abstract the order state asStateClass:

abstract class State {
    Order order;

    // Change the status
    final void changeState(State state){
        order.setState(state);
    }

    void setOrder(Order order) {
        this.order = order;
    }

    // All operations are not supported by default, subclass override
    public void pay(a){Throw exception current status does not support this operation}// omit other methods
}
Copy the code

Each state is only responsible for its own operation logic, starting with “to be paid”, which supports two operations: payment or cancellation.

class WaitPayState extends State{

    @Override
    public void pay(a) {
        print("Order payment.");
        changeState(new WaitSendState());
    }

    @Override
    public void cancel(a) {
        print("Cancel order.");
        changeState(newFinishedState()); }}Copy the code

Waiting shipment status: WaitSendState.

class WaitSendState extends State {

    @Override
    public void send(a) {
            print("The seller delivers.");
            changeState(newWaitReceiveState()); }}Copy the code

No more code will be posted here for other state classes.

The optimized Order class becomes very simple, with all operations entrusted to the state class and only responsible for maintaining the state switch:

class Order {
    State state;

    Order() {
        // Default to be paid
        state = new WaitPayState();
        state.setOrder(this);
    }

    void setState(State state) {
        state.setOrder(this);
        this.state = state;
    }
    // Order operations call state to complete
}
Copy the code

The client calls remain unchanged and the program runs the same, but the optimized Order class becomes very simple, eliminating the if judgment. If you want to add an Order State, it is also easy to derive the State subclass. Now each state is a separate class, and that class will change only if the factors associated with that state change, in accordance with Demeter’s law, perfect.

This is state mode!

2. Definition of state mode

Allowing an object to change its behavior when its internal state changes, the object appears to have changed its class.

State pattern generic class diagram

  • State: Abstract State class that defines the State of an object and encapsulates the Context object for State switching.
  • ConcreteState: ConcreteState class. Each state class must fulfill two responsibilities: 1. Operation logic of this state. 2. Transition of the state.
  • Context: An environment role that defines the interface required by the client and is responsible for state switching.

The core of state mode is encapsulation. The change of state causes the change of behavior. From the outside, it looks like the corresponding class of this object is changed, because the Context class itself does not realize the business logic, and all operations are entrusted to the state class.

3. Pros and cons of state patterns

advantages

  1. Clear structure, eliminate if judgment, code readability improved.
  2. In line with the “open and closed principle” and “single responsibility principle”, each state is a class, to extend the state to derive a subclass, to modify the state to modify the corresponding state class.
  3. Good encapsulation, shielding the client from internal implementations of state.

Disadvantages You need to write a class for each state, and the number of classes can swell if there are many states.

4. To summarize

“State mode” applies to scenarios where the behavior of an object is limited by its state. Different states determine the behavior of the object. It can eliminate the IF branch and improve the readability and maintainability of the code. It is important to note that if too many object states cause the number of classes to swell, developers need to weigh the pros and cons.