Chapter 17 Behavioral Pattern—State Pattern


Behavioral patterns are used to describe the complex flow control of programs at runtime, that is, to describe how multiple classes or objects cooperate with each other to complete tasks that a single object cannot complete alone. It involves the allocation of responsibilities between algorithms and objects. Behavioral patterns are divided into class behavior patterns and object behavior patterns:

  • Class behavior pattern: using inheritance mechanism to dispatch behavior between classes

  • Object behavior pattern: Use composition or aggregation to distribute behavior among objects

Since the combination relationship or aggregation relationship is less coupled than the inheritance relationship and satisfies the "principle of composite reuse", the object behavior pattern has greater flexibility than the class behavior pattern.

Behavioral patterns are divided into:

  • template method pattern
  • strategy pattern
  • command mode
  • chain of responsibility model
  • state mode
  • Observer pattern
  • intermediary pattern
  • iterator pattern
  • visitor mode
  • Memo mode
  • interpreter mode

The above 11 behavioral patterns, except for the template method pattern and the interpreter pattern, which are quasi-behavioral patterns, the others all belong to the object behavioral pattern.

state mode

**State mode:**For stateful objects, extract complex "judgment logic" into different state objects (the 允许状态对象在其内部状态发生改变时改变其行为object seems to have modified its class)

  • In the State Pattern, the behavior of a class changes based on its state. This type of design pattern is a behavioral pattern.
    • Allows an object to change its behavior when its internal state changes, making the object appear as if its class has been modified.

solved problem

Counterexample

The status of an elevator is controlled through buttons. An elevator has an open-door state, a closed-door state, a stopped state, and a running state. Every state change may need to be updated based on other states. For example, if the elevator door is currently in a running state, the door opening operation cannot be performed, but if the elevator door is in a stopped state, the door opening operation can be performed. The class diagram is as follows:

insert image description here

Elevator interface

public interface ILift {
    
    
    //定义四个电梯状态的常量
    int OPENING_STATE = 1;
    int CLOSING_STATE = 2;
    int RUNNING_STATE = 3;
    int STOPPING_STATE = 4;
    //设置电梯状态的功能
    void setState(int state);
    //电梯的操作功能
    void open();
    void close();
    void run();
    void stop();
}

Elevator implementation

public class Lift implements  ILift{
    
    
    // 声明一个记录当前电梯的状态
    private int state;

    @Override
    public void setState(int state) {
    
    
        this.state = state;
    }

    @Override
    public void open() {
    
    
        switch (state){
    
    
            case OPENING_STATE:
                //什么事也不做
                break;
            case CLOSING_STATE:
                System.out.println("电梯打开了...");
                //设置当前电梯状态为开启状态
                setState(OPENING_STATE);
                break;
            case STOPPING_STATE:
                System.out.println("电梯打开了...");
                //设置当前电梯状态为开启状态
                setState(OPENING_STATE);
                break;
            case RUNNING_STATE:
                //什么事都不做
                break;
        }
    }

    @Override
    public void close() {
    
    
        switch (this.state) {
    
    
            case OPENING_STATE:
                System.out.println("电梯关门了。。。");//只有开门状态可以关闭电梯门,可以对应电梯状态表来看
                this.setState(CLOSING_STATE);//关门之后电梯就是关闭状态了
                break;
            case CLOSING_STATE:
                //do nothing //已经是关门状态,不能关门
                break;
            case RUNNING_STATE:
                //do nothing //运行时电梯门是关着的,不能关门
                break;
            case STOPPING_STATE:
                //do nothing //停止时电梯也是关着的,不能关门
                break;
        }
    }

    @Override
    public void run() {
    
    
        switch (this.state) {
    
    
            case OPENING_STATE://电梯不能开着门就走
                //do nothing
                break;
            case CLOSING_STATE://门关了,可以运行了
                System.out.println("电梯开始运行了。。。");
                this.setState(RUNNING_STATE);//现在是运行状态
                break;
            case RUNNING_STATE:
                //do nothing 已经是运行状态了
                break;
            case STOPPING_STATE:
                System.out.println("电梯开始运行了。。。");
                this.setState(RUNNING_STATE);
                break;
        }
    }

    @Override
    public void stop() {
    
    
        switch (this.state) {
    
    
            case OPENING_STATE: //开门的电梯已经是是停止的了(正常情况下)
                //do nothing
                break;
            case CLOSING_STATE://关门时才可以停止
                System.out.println("电梯停止了。。。");
                this.setState(STOPPING_STATE);
                break;
            case RUNNING_STATE://运行时当然可以停止了
                System.out.println("电梯停止了。。。");
                this.setState(STOPPING_STATE);
                break;
            case STOPPING_STATE:
                //do nothing
                break;
        }
    }
}

problem analysis:

  • A large number of switch...case judgments are used (the same is true for if...else), which makes the program less readable.
  • Scalability is poor. If a new power-off state is added, we need to modify the above judgment logic

structure

  • Environment (Context) role: Also called context, it defines the interface required by the client program, maintains a current state , and delegates state-related operations to the current state object for processing.
  • Abstract state (State) role: Define an interface to encapsulate the behavior corresponding to a specific state in the environment object. Each state implements state transfer by holding a reference to Context.
  • Concrete State role: implements the behavior corresponding to the abstract state.

example

insert image description here

Abstract state class :

public abstract class LiftState {
    // 声明环境角色类变量
    protected Context context;
    public void setContext(Context context) {
        this.context = context;
    }
    // 电梯开启操作
    public abstract void open();
    // 电梯关闭操作
    public abstract void close();
    // 电梯运行操作
    public abstract void run();
    // 电梯停止操作
    public abstract void stop();
}

Specific status categories : elevator door opening status, elevator running status, elevator stopped status, elevator closing status

public class OpeningState extends LiftState {
    
    
    // 当前状态要执行的方法
    public void open() {
    
    
        System.out.println("电梯开启。。。");
    }
    public void close() {
    
    
        // 修改状态
        super.context.setLiftState(Context.CLOSING_STATE);
        // 修改环境
        super.context.close();
    }
    public void run() {
    
    }
    public void stop() {
    
    }
}
public class ClosingState extends LiftState {
    
    
    // 当前状态要执行的方法
    public void close() {
    
    
        System.out.println("电梯门关闭...");
    }
    // 关闭 -> 开启
    public void open() {
    
    
        super.context.setLiftState(Context.OPENING_STATE);
        super.context.open();
    }
    // 关闭 -> 运行
    public void run() {
    
    
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.run();
    }
    // 关闭 -> 停止
    public void stop() {
    
    
        super.context.setLiftState(Context.STOPPING_STATE);
        super.context.stop();
    }
}
public class RunningState extends LiftState{
    
    
    @Override
    // 运行时无法开门
    public void open() {
    
    

    }

    @Override
    // 运行时门是关的
    public void close() {
    
    

    }

    @Override
    // 当前状态要执行的方法
    public void run() {
    
    
        System.out.println("电梯正在运行...");
    }

    @Override
    // 运行 -> 停止
    public void stop() {
    
    
        super.context.setLiftState(Context.STOPPING_STATE);
        super.context.stop();
    }
}
public class StoppingState extends LiftState{
    
    
    @Override
    // 停止 -> 开门(委托给ClosingState子类执行)
    public void open() {
    
    
        super.context.setLiftState(Context.OPENING_STATE);
        super.context.getLiftState().open();
    }

    @Override
    // 停止 -> 关门(委托给OpeningState子类执行)
    public void close() {
    
    
        super.context.setLiftState(Context.CLOSING_STATE);
        super.context.getLiftState().close();
    }

    @Override
    // 停止 -> 运行(委托给 RunningState子类执行)
    public void run() {
    
    
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.getLiftState().run();
    }

    @Override
    // 当前状态要执行的方法
    public void stop() {
    
    
        System.out.println("电梯停止了...");
    }
}

Environmental role class :

public class Context {
    
    
    // 定义对应状态对象的常量
    public final static OpeningState OPENING_STATE = new OpeningState();
    public final static ClosingState CLOSING_STATE = new ClosingState();
    public final static RunningState RUNNING_STATE = new RunningState();
    public final static StoppingState STOPPING_STATE = new StoppingState();

    // 定义一个当前电梯状态变量
    private LiftState liftState;
    public LiftState getLiftState() {
    
    
        return liftState;
    }

    // 设置当前状态对象
    public void setLiftState(LiftState liftState) {
    
    
        this.liftState = liftState;
        // 设置当前状态对象中的Context对象
        this.liftState.setContext(this);
    }

    public void open() {
    
    
        this.liftState.open();
    }

    public void close() {
    
    
        this.liftState.close();
    }

    public void run() {
    
    
        this.liftState.run();
    }

    public void stop() {
    
    
        this.liftState.stop();
    }
}

test

public class Client {
    
    
    public static void main(String[] args) {
    
    
        // 创建环境角色对象
        Context context = new Context();
        // 设置当前电梯装填
        context.setLiftState(new ClosingState());

        context.open();
        context.run();
        context.close();
        context.stop();
    }
}

Problems

advantage:

  • 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 object state to change the object's behavior.

  • Allows state transition logic to be integrated with the state object instead of a huge conditional statement block.

shortcoming:

  • The use of state mode will inevitably increase the number of system classes and objects.
  • The structure and implementation of the state pattern are relatively complex. If used improperly, it will lead to confusion in the program structure and code.
  • The state model does not support the "open-close principle" very well.

scenes to be used

  • Consider using the State pattern when an object's behavior depends on its state, and it must change its behavior at runtime based on the state.
  • When an operation contains a huge branch structure, and these branches are determined by the state of the object.

The difference between state pattern and strategy pattern

  • Status emphasizes different states. Different states have different things to do (happy -> buy KFC and treat yourself, unhappy -> take leave tomorrow and not go to work), focusing on being happy or unhappy, and happy or unhappy, specifically It doesn’t matter what you do. If you’re not happy, you can resign tomorrow.

    • The state pattern determines behavior based on the state. A series of different behaviors will be taken under different states (the implementation of all methods may change)
      • The state pattern helps a class display different behaviors in different states.
    • For example, in status mode, if my status today is "unhappy", then a series of behaviors such as getting up, eating, reading, and sleeping will be affected.
  • The strategy pattern only focuses on the specific execution process of a certain behavior. For example, how do I read a book? Standing, sitting or lying down? The emphasis is on the "steps of doing", that is, the behavior and the algorithm itself

    • However, the strategy pattern does not have the concept of state. It directly focuses on the implementation plan of the behavior (the specific execution process of a certain behavior), and focuses more on the changing and unchanged parts of the project.
    • The Strategy pattern encapsulates a set of related algorithms that allows clients to use interchangeable behaviors at runtime.
  • The strategy implementation can be passed as a parameter to the object using it, such as Collections.sort(), whose parameter contains a Comparator strategy. On the other hand, state is part of the Context object itself, and the Context object moves from one state to another over time.

  • In the state pattern, each state implements state transition by holding a reference to the Context ; but each strategy does not hold a reference to the Context, they are only used by the Context.

    • The order of state transitions is well defined in the state pattern; this is not required in the strategy pattern: the client is free to choose any strategy.
  • The last but most important difference is that the change of strategy is done by the Client; while the change of state is done by the Context or the state itself.

  • Some common examples of strategy patterns are encapsulating algorithms, such as sorting algorithms, encryption algorithms, or compression algorithms. If you see that your code needs to use different types of related algorithms, consider using the Strategy pattern. And identifying when to use the State pattern is simple: if you need to manage states and state transitions, but don't want to use a lot of nested conditional statements, this is it.

The state also involves the process of state switching. This process can be determined internally by the state and does not require external intervention. This is different from the strategy model. The main change factors of the strategy are affected by the outside world.

The logical judgment code of more than three levels of if-else statements can use guard statements, strategy mode, and state mode.

public void today(){
    
    
	if(isBusy()){
    
    
		System.out.println("no time")
	}
}
  • The guard statement is the method of ending early

Guess you like

Origin blog.csdn.net/qq_50985215/article/details/130970174