Design mode 2 status mode

definition

State mode: when the internal state of an object changes, it is allowed to change its behavior. The object looks like it has changed its class.
And strategy mode are twins.
It mainly solves the problem when the conditional expression controlling the state transition of an object is too complex. That is, the state judgment logic is transferred to a series of classes that identify different states.

Applicable scenario

  1. A scene in which behavior changes with state changes. For example, if a designer performs the same behavior with different permissions, the results will be different.
  2. Replace the condition and branch statement, and realize the condition judgment processing by extending the subclass..

Main role

  1. Context: the object containing the state. It can process some requests. The final response of these requests will be related to the state.
  2. State: it defines the behavior set of each state, which will be used in the Context.
  3. Concrete state: a concrete state class that implements related behaviors

    State can use interfaces or abstract classes. Using abstract classes can more easily extend new methods.

give an example

Candy selling machine from Head FIRST design pattern.
Realize a candy machine, put in two coins, you can spit out a sugar, during which you can refund the coin.
Therefore, the candy machine has the following states: no coins, coins, candy sold, candy sold out. If you use logical control statements to realize this function, you need a large number of if else to control, and it is difficult to expand. In the future, you need to change all control statements to add a new state.

First let's create a State interface

interface State{
    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
}

Then, each state in the design is encapsulated into a class, and each implements the state interface. Take NoQuarterState as an example:

class NoQuarterState implements State{

    GumballMachine gumballMachine;
    public NoQuarterState(GumballMachine gumballMachine){
        this.gumballMachine=gumballMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("If someone invested 25 cents, we printed a message saying we accepted 25 cents," +
                "Then change the state of the machine to HasQuarterState");
        gumballMachine.setState(gumballMachine.getHasQuarterState());
    }

    @Override
    public void ejectQuarter() {
        System.out.println("If you don't give the money, you don't ask for a refund");
    }

    @Override
    public void turnCrank() {
        System.out.println("If you don't give money, you can't ask for candy");
    }

    @Override
    public void dispense() {
        System.out.println("If we don't get the money, we can't distribute candy.");
    }
}

Implement the context class,

class GumballMachine {
	//Define status
    State soldOutState;
    State noQuarterState;
    State hasQuarterState;
    State soldState;

	//current state
    State state = soldState;
    //Candy margin
    int count = 0;

    public GumballMachine_(int count) {
    	//Will own
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldOutState = new SoldState(this);
        this.count = count;
        if (count > 0) {
            state = noQuarterState;
        }
    }
    //Put in a coin
    public void insertQuarter() {
        state.insertQuarter();
    }
	//Refund
    public void ejectQuarter() {
        state.ejectQuarter();
    }
	//Turn the handle
    public void turnCrank(){
        state.turnCrank();
        state.dispense();
    }
    //Set current state
    void setState(State state){
        this.state=state;
    }
    //Release candy
    void releaseBall(){
        System.out.println();
        if(count!=0){
            count=count-1;
        }
    }
    public State getSoldState() {
        return soldState;
    }
    public State getHasQuarterState() {
        return hasQuarterState;
    }
    public State getNoQuarterState() {
        return noQuarterState;
    }
    public State getSoldOutState() {
        return soldOutState;
    }
}

Similarly, continue to implement other state classes
If you want to add a new status: one out of ten times, you only need to make the following changes:
First, add a state to the GumballMahine class

class GumballMachine {
	//New definition status
    State winnerState

Implement this winnerState:

class WinnerState implements  State{
    GumballMachine_ gumballMachine;

    public WinnerState(GumballMachine_ gumballMachine) {
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("Inappropriate action");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("Inappropriate action");
    }

    @Override
    public void turnCrank() {
        System.out.println("Inappropriate action");
    }

    @Override
    public void dispense() {
        //If there's a second candy, we'll release it.
        gumballMachine.releaseBall();
        if(gumballMachine.getCount()==0){
            gumballMachine.setState(gumballMachine.getSoldState());
        }else{
            gumballMachine.releaseBall();
            if(gumballMachine.getCount()>0){
                gumballMachine.setState(gumballMachine.getNoQuarterState());
            }else{
                gumballMachine.setState(gumballMachine.getSoldOutState());
            }
        }
    }
}

Next, you only need to add an entry into winnerstate in HasQuarterState.

class HasQuarterState implements State {
	//Generate random number
    Random random = new Random(System.currentTimeMillis());
    GumballMachine_ gumballMachine;

    public HasQuarterState(GumballMachine_ gumballMachine) {
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("This is an inappropriate action for this state");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("Withdraw the customer's 25 cents and switch the status to NoQuarterState state");
        gumballMachine.setState(gumballMachine.getNoQuarterState());
    }

    @Override
    public void turnCrank() {
        System.out.println("When the crank turns, we change the state to SoldState");
        int winner = random.nextInt(10);
        //One tenth probability of entering winner mode
        if ((winner == 0) && (gumballMachine.getCount() > 1)) {
            gumballMachine.setState(gumballMachine.getWinnerState());
        } else {
            gumballMachine.setState(gumballMachine.getSoldState());
        }
    }

    @Override
    public void dispense() {
        System.out.println("This is another inappropriate action of this state");
    }
}

be careful

The environment class (candy machine) of the above code can only have one instance, because the state object holds the environment class. If you want to share the state object, you can set the state object to static and pass in the reference of Context in handler(). For example, refer to the third blog: (21) Detailed explanation of status mode (DOTA version) Examples in.

Relationship with policy pattern

Strategy mode
As like as two peas can be found, the class diagram is similar to the policy pattern, but the difference between the two modes is their intention: the policy pattern usually configures the Context class by actions or algorithms (the client does not need to know the change of Contex); the state mode allows Context to change behavior with the change of state.

Advantages and disadvantages

advantage

  • The structure is clear, the complexity of the program is avoided, and the maintainability of the system is improved
  • It embodies the principle of opening and closing and the principle of single responsibility. Each state is a subclass, which is highly consistent with the principle of single responsibility. Expanding the state only needs to add subclasses, which is the embodiment of the principle of opening and closing. Encapsulation is also very consistent with Demeter's law
  • The customer will not directly interact with the state. The customer only needs to put in coins, turn the handle and other actions, and does not need to know about the machine and others

shortcoming

If a transaction has many states, it will cause too many subclasses. It is necessary to measure whether the state pattern is used when the project is used

reference resources

On Design Pattern -- state pattern
State mode of Head First design mode
(21) Detailed explanation of status mode (DOTA version)

Keywords: Design Pattern

Added by james_kirk on Tue, 12 Oct 2021 08:24:46 +0300