The Java design pattern of reverie triggered by the chime of Zeng Houyi: State Pattern

Example

A class provides multiple external behaviors, and at the same time, objects of this class have multiple states. The external
behaviors in different states are different. How do we design this class so that it can flexibly extend the state?
Take the automatic beverage vending machine as an example to develop a program:

  1. Users can perform payment, refund, purchase, and pickup operations on the beverage machine
  2. In different states, these four operations will have different performances.
    For example: What will happen if the user performs refund, purchase, or pickup when the user does not pay

Simple example

public class DrinksSellingMachine {
    
    
    
    //未支付
    final static int NO_PAY = 0;
    //已支付
    final static int PAY = 1;
    //待取货
    final static int SOLD = 2;
    //已售罄
    final static int SOLD_OUT = 4;

    //当前状态
    private int state = SOLD_OUT;
    //当前库存
    private int store;

    public DrinksSellingMachine(int store) {
    
    
        this.store = store;
        if(this.store > 0) {
    
    
            this.state = NO_PAY;
        }
    }

    /** 支付 */
    public void pay() {
    
    
        switch (this.state) {
    
    
            case NO_PAY :
                System.out.println("支付成功,请确定购买饮料。");
                this.state = PAY;
                break;
            case PAY :
                System.out.println("已支付成功,请确定购买饮料。");
                break;
            case SOLD :
                System.out.println("待取饮料中,请稍后购买。");
                break;
            case SOLD_OUT :
                System.out.println("饮料已售罄,不可购买。");
                break;
        }

    }

    /** 退款 */
    public void refund() {
    
    
        switch (this.state) {
    
    
            case NO_PAY :
                System.out.println("尚未支付,请不要乱按。");
                break;
            case PAY :
                System.out.println("退款成功。");
                this.state = NO_PAY;
                break;
            case SOLD :
                System.out.println("已购买,请取用。");
                break;
            case SOLD_OUT :
                System.out.println("饮料已售罄,不可购买。");
                break;
        }
    }

    /** 购买 */
    public void buy() {
    
    
        switch (this.state) {
    
    
            case NO_PAY :
                System.out.println("尚未支付,请不要乱按。");
                break;
            case PAY :
                System.out.println("购买成功,请取用。");
                this.state = SOLD;
                break;
            case SOLD :
                System.out.println("已购买,请取用。");
                break;
            case SOLD_OUT :
                System.out.println("饮料已售罄,不可购买。");
                break;
        }
    }

    /** 取货 */
    public void getGoods() {
    
    
        switch (this.state) {
    
    
            case NO_PAY :
                System.out.println("尚未支付,请不要乱按。");
                break;
            case PAY :
                System.out.println("已购买,请取用。");
            case SOLD :
                System.out.println("正在出货中,请等待三秒。");
                this.store--;
                if (this.store == 0) {
    
    
                    this.state = SOLD_OUT;
                } else {
    
    
                    this.state = NO_PAY;
                }
                break;
            case SOLD_OUT :
                System.out.println("饮料已售罄,不可购买。");
                break;
        }
    }
}

Seeing a large piece of code above, I feel very confused. If you need to expand the state now, you need to modify the method under each operation, which will be a troublesome thing.
How to expand the state flexibly?
Four operations It will not change, but the state can be flexibly changed. The following improved code:

Improve the code

Define an interface, each state needs to implement the interface:

public interface State {
    
    

    /** 支付 */
    void pay();

    /** 退款 */
    void refund();

    /** 购买 */
    void buy();

    /** 取货 */
    void getGoods();
}

Unpaid status implementation class:

public class NoPayState implements State {
    
    

    private NewDrinksSellingMatchine matchine;

    public NoPayState(NewDrinksSellingMatchine matchine) {
    
    
        this.matchine = matchine;
    }

    @Override
    public void pay() {
    
    
        System.out.println("支付成功,请确定购买饮料。");
        this.matchine.state = this.matchine.PAY;
    }

    @Override
    public void refund() {
    
    
        System.out.println("尚未支付,请不要乱按。");
    }

    @Override
    public void buy() {
    
    
        System.out.println("尚未支付,请不要乱按。");
    }

    @Override
    public void getGoods() {
    
    
        System.out.println("尚未支付,请不要乱按。");
    }
}

Payment status implementation class:

public class PayState implements State{
    
    

    private NewDrinksSellingMatchine matchine;

    public PayState(NewDrinksSellingMatchine matchine) {
    
    
        this.matchine = matchine;
    }

    @Override
    public void pay() {
    
    
        System.out.println("已支付成功,请确定购买饮料。");
    }

    @Override
    public void refund() {
    
    
        System.out.println("退款成功。");
        this.matchine.state = this.matchine.NO_PAY;
    }

    @Override
    public void buy() {
    
    
        System.out.println("已购买,请取用。");
        this.matchine.state = this.matchine.SOLD;
    }

    @Override
    public void getGoods() {
    
    
        System.out.println("请先确定购买。");
    }
}

The other two states are not shown here, they are similar to the above two

public class NewDrinksSellingMatchine {
    
    

    final State NO_PAY, PAY, SOLD, SOLD_OUT;

    State state;

    int store;

    public NewDrinksSellingMatchine(int store) {
    
    
        NO_PAY = new NoPayState(this);
        PAY = new PayState(this);
        SOLD = new SoldState(this);
        SOLD_OUT = new SoldOutState(this);
        this.store = store;
        if(this.store > 0) {
    
    
            this.state = NO_PAY;
        }
    }

    public void pay() {
    
    
        this.state.pay();
    }

    public void refund() {
    
    
        this.state.refund();
    }

    public void buy() {
    
    
        this.state.buy();
    }

    public void getGoods() {
    
    
        this.state.getGoods();
    }
}

After the improvement, if you need to expand the state, you only need to implement the State interface.

State mode

definition

A class provides multiple behaviors externally, and at the same time, objects of this type have multiple states, and the external
behaviors in different states are different.

intention

Allows the object to change its behavior when its internal state changes, the object looks like it has modified its class

Mainly solve the problem

The behavior of an object depends on its state (attribute), and its related behavior can be changed according to its state change

When to use

The code contains a large number of conditional statements related to the state of the object

Pros and cons

advantage:

  1. Encapsulates the conversion rules
  2. Put all the behaviors related to a certain state into a class, and you can easily add new states, you only need to change the state of the object to change the behavior of the object
  3. Allow the state transition logic to be integrated with the state object, instead of a huge block of conditional statements
  4. Multiple environment objects can share a state object, thereby reducing the number of objects in the system

Disadvantages:

  1. The use of state mode will inevitably increase the number of system classes and objects
  2. The structure and implementation of the state mode are relatively complicated, if used improperly, it will lead to confusion in the structure and code of the program
  3. The state mode does not support the "opening and closing principle" very well. For the state mode that can switch states, adding new state classes needs to modify the source code responsible for state transition, otherwise it will not be able to switch to the new state and modify a certain state. The behavior of the state class also needs to modify the source code of the corresponding class

Class diagram:
Insert picture description here
roles involved:

  1. Abstract state (State) role: define an interface to encapsulate the behavior corresponding to a specific state of the Context object
  2. Concrete state (ConcreteState) role: each concrete state class implements the behavior corresponding to a state of the environment (Context)
  3. Context role: Define the interface that the client is interested in, and keep an instance of a specific state class, which gives the current state of the environment object

The corresponding code is as follows:
State interface:

public interface State {
    
    

    void sampleOperation();
}

ConcreteState class:

public class ConcreteState implements State {
    
    
    @Override
    public void sampleOperation() {
    
    

    }
}

Context class:

public class Context {
    
    

    private State state;

    public void sampleOperation() {
    
    
        this.state.sampleOperation();
    }

    public void setState(State state) {
    
    
        this.state = state;
    }
}

Zeng Hou Yi Chime

The Zenghouyi chime, unearthed in Hubei in 1979, a set of 65 pieces, the total range spans 5 octaves, and 12 semitones are complete. Each clock emits a different tone.
Let’s take the Zenghouyi chime as an example, let’s get familiar with it. State mode:
Insert picture description here
The interface that every clock needs to implement:

public interface ClockState {
    
    

    /** 打击钟 */
    void blow();

    void otherClock();
}

The specific realization of each clock:

public class ClockConcreteStateC implements ClockState {
    
    
    @Override
    public void blow() {
    
    
        System.out.println("钟C被打击");
    }

    @Override
    public void otherClock() {
    
    
        System.out.println("钟A B没有被打击");
    }
}
public class ClockConcreteStateB implements ClockState {
    
    
    @Override
    public void blow() {
    
    
        System.out.println("钟B被打击");
    }

    @Override
    public void otherClock() {
    
    
        System.out.println("钟A C没有被打击");
    }
}
public class ClockConcreteStateA implements ClockState {
    
    
    @Override
    public void blow() {
    
    
        System.out.println("钟A被打击");
    }

    @Override
    public void otherClock() {
    
    
        System.out.println("钟B C没有被打击");
    }
}

Once Zeng Houyi chimes the bell, the musician chooses a bell to strike it, and each tone of each bell represents a state:

public class ClockContext {
    
    

    private ClockState state;

    public void blow() {
    
    
        this.state.blow();
        this.state.otherClock();
    }

    public void setState(ClockState state) {
    
    
        this.state = state;
    }
}

Test category:

public class Test {
    
    
    public static void main(String[] args) {
    
    
        ClockContext context = new ClockContext();
        context.setState(new ClockConcreteStateA());
        context.blow();
    }
}

Insert picture description here
Class diagram:
Insert picture description here
Note: Use the state mode when the behavior is constrained by the state, and the state does not exceed 5

State mode-command mode-strategy mode

Strategy mode: The focus is on the realization of multiple algorithms for a behavior, interchangeable algorithms, such as discounts and discounts are algorithms, you can choose one of them to buy, buy, buy

Command mode: The focus is on providing flexible execution methods for multiple actions, such as the milk tea above. Each customer's order to buy milk tea is an action, and different milk tea production methods are different, so you need to be flexible to make milk tea

State mode: applied to the case of a state machine

Design Principles:

  • Distinguish change from unchanged, isolate change
  • Interface-oriented programming
  • Use more combination, less inheritance

Guess you like

Origin blog.csdn.net/qq_34365173/article/details/108110024