Writing in the front

  • Take notes on learning design patterns
  • Improve the flexible use of design patterns

Learning to address

www.bilibili.com/video/BV1G4…

www.bilibili.com/video/BV1Np…

Refer to the article

C.biancheng.net/view/1317.h…

Program source code

Gitee.com/zhuang-kang…

24. Interpreter mode

Design software to do addition and subtraction calculations. Our first idea was to use utility classes to provide the corresponding addition and subtraction utility methods.

// Add two integers
public static int add(int a,int b){
    return a + b;
}

// Add two integers
public static int add(int a,int b,int c){
    return a + b + c;
}

// Add n integers
public static int add(Integer ... arr) {
    int sum = 0;
    for (Integer i : arr) {
        sum += i;
    }
    return sum;
}
Copy the code

The above form is simple and finite, and if the form changes too much, it doesn’t work because of addition and subtraction, two operators and numbers can be combined in an infinite number of ways. Such as 1+2+3+4+5, 1+2+3-4 and so on.

Clearly, there is a need for a translation recognition machine that can parse legitimate sequences of numbers and + – symbols. If you treat both operators and numbers as nodes, you can read and parse them node by node, which is the interpreter mode of thinking.

24.1 Definition and characteristics of the interpreter pattern

Definition of the Interpreter pattern: ** Define a language for the parse object, define its grammatical representation, and design a parser to interpret sentences in the language. ** That is to say, analyze the application instances in the same way as the compiled language. This pattern implements an interface for grammar expression processing that interprets a particular context.

The concepts of grammar and sentence mentioned here are the same as described in the compilation principle. “grammar” refers to the grammatical rules of the language, while “sentence” is an element of the language set. For example, there are many sentences in Chinese, “I am Chinese” is one of them, can be used a grammar tree to describe sentences in the language intuitively.

The interpreter pattern is a behavioral pattern with the following major advantages.

  1. Good scalability. Because classes are used in the interpreter schema to represent the grammar rules of the language, the grammar can be changed or extended through mechanisms such as inheritance.
  2. Easy to implement. Each expression node class in the syntax tree is similar, so the grammar is easier to implement.

The main disadvantages of the interpreter pattern are as follows.

  1. Execution efficiency is low. Interpreter mode usually uses a lot of loops and recursive calls, which are slow and cumbersome to debug when the sentences to be interpreted are complex.
  2. It causes class inflation. Each rule in the interpreter pattern needs to define at least one class. When there are many grammar rules, the number of classes increases dramatically, making it difficult to manage and maintain the system.
  3. There are fewer scenarios to apply. In software development, there are very few applications where language grammars need to be defined, so this pattern is rarely used.

24.2 Structure and implementation of interpreter pattern

24.2.1 Structure of the interpreter pattern

  • Abstract Expression role: Defines the interface of the interpreter and defines the interpretation operations of the interpreter, mainly including the interpretation method interpret().
  • Terminal Expression role: A subclass of abstract expressions used to implement terminal-related operations ina grammar. Each terminator ina grammar has a specific terminal-expression corresponding to it.
  • Nonterminal Expression role: Also a subclass of abstract expressions, used to implement non-terminal related operations ina grammar, where each rule corresponds to a non-terminal Expression.
  • Context role: Usually contains data required by individual interpreters or a common function used to pass data shared by all interpreters, from which subsequent interpreters can obtain these values.
  • Client: The main task is to translate the sentence or expression to be analyzed into an abstract syntax tree described by the interpreter object, and then call the interpreter’s interpretation methods, which can also be accessed indirectly through the environment role.

24.2.2 Code implementation

Relationship between the class diagram

AbstractExpression

package com.zhuang.interpreter;

/ * * *@Classname AbstractExpression
 * @DescriptionAbstract roles@Date2021/3/31 * a quarter to ten@Created by dell
 */

public abstract class AbstractExpression {
    public abstract int interpret(Context context);
}
Copy the code

Value

package com.zhuang.interpreter;

/ * * *@Classname Value
 * @DescriptionTerminator expression role *@Date2021/3/31 mount zalmon *@Created by dell
 */

public class Value extends AbstractExpression {

    private int value;

    public Value(int value) {
        this.value = value;
    }

    @Override
    public int interpret(Context context) {
        return value;
    }

    @Override
    public String toString(a) {
        return newInteger(value).toString(); }}Copy the code

Plus

package com.zhuang.interpreter;

/ * * *@Classname Plus
 * @DescriptionNon-terminal expression role addition expression *@Date2021/3/31.so *@Created by dell
 */

public class Plus extends AbstractExpression {

    private AbstractExpression left;
    private AbstractExpression right;

    public Plus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        return left.interpret(context) + right.interpret(context);
    }

    @Override
    public String toString(a) {
        return "(" + left.toString() + "+" + right.toString() + ")"; }}Copy the code

Minus

package com.zhuang.interpreter;

/ * * *@Classname Minus
 * @DescriptionNon-terminal expression role subtraction expression *@Date2021/3/31.so *@Created by dell
 */

public class Minus extends AbstractExpression {
    private AbstractExpression left;
    private AbstractExpression right;

    public Minus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        return left.interpret(context) - right.interpret(context);
    }

    @Override
    public String toString(a) {
        return "(" + left.toString() + "-" + right.toString() + ")"; }}Copy the code

Variable

package com.zhuang.interpreter;

/ * * *@Classname Variable
 * @DescriptionTerminator expression Role variable expression *@Date2021/3/31.so *@Created by dell
 */

public class Variable extends AbstractExpression {

    private String name;

    public Variable(String name) {
        this.name = name;
    }

    @Override
    public int interpret(Context context) {
        return context.getValue(this);
    }

    @Override
    public String toString(a) {
        returnname; }}Copy the code

Context

package com.zhuang.interpreter;

import java.util.HashMap;
import java.util.Map;

/ * * *@Classname Context
 * @DescriptionEnvironment *@Date2021/3/31.so *@Created by dell
 */

public class Context {
    private Map<Variable, Integer> map = new HashMap<Variable, Integer>();

    public void assign(Variable var, Integer value) {
        map.put(var, value);
    }

    public int getValue(Variable var) {
        Integer value = map.get(var);
        returnvalue; }}Copy the code

Client

package com.zhuang.interpreter;

/ * * *@Classname Client
 * @DescriptionInterpretation right mode test class *@Date2021/3/31.so *@Created by dell
 */

public class Client {
    public static void main(String[] args) {
        Context context = new Context();

        Variable a = new Variable("a");
        Variable b = new Variable("b");
        Variable c = new Variable("c");
        Variable d = new Variable("d");
        Variable e = new Variable("e");

        context.assign(a, 2);
        context.assign(b, 3);
        context.assign(c, 4);
        context.assign(d, 5);
        context.assign(e, 6);

        AbstractExpression expression = new Minus(new Plus(new Plus(new Plus(a, b), c), d), e);

        System.out.println(expression + "="+ expression.interpret(context)); }}Copy the code

24.3 Application Scenarios of interpreter Mode

  • When the grammar of the language is simple and execution efficiency is not the key issue.
  • When the problem is repeated and can be expressed in a simple language.
  • When a language needs to interpret execution and the sentences in the language can be represented as an abstract syntax tree.

25. State mode

25.1 Definition and Features of the Status Mode

The definition of State mode: For stateful objects, the complex “judgment logic” is extracted into different State objects, allowing the State object to change its behavior when its internal State changes.

The state pattern is an object behavior pattern with the following major advantages.

  1. The structure is clear, and the state mode localizes the behaviors related to a specific state into a state, and separates the behaviors of different states to meet the “single responsibility principle”.
  2. State transitions are visualized to reduce dependencies between objects. Introducing different states into separate objects makes state transitions more explicit and reduces the interdependence of objects.
  3. The status class has clear responsibilities and is conducive to the expansion of the program. You can easily add new states and transitions by defining new subclasses.

The main disadvantages of the state mode are as follows.

  1. The use of state patterns inevitably increases the number of classes and objects in the system.
  2. The structure and implementation of state pattern are complicated, if used improperly, it will lead to confusion of program structure and code.
  3. State modes do not support the on/off principle very well. For a state mode that can switch states, adding a new state class requires modifying the source code responsible for the state transition, otherwise it cannot switch to the new state, and modifying the behavior of a state class also requires modifying the source code of the corresponding class.

25.2 Structure and implementation of state mode

【例】 The state of an elevator can be controlled by a button. An elevator has the state of opening, closing, stopping and running. For each state change, it is possible to update the processing based on other states. For example, an elevator door cannot be opened if it is now in the running state, whereas an elevator door can be opened if it is in the stopped state

public interface ILift {
    // There are four states of the elevator
    // Open the door
    public final static int OPENING_STATE = 1;
    // Close the door
    public final static int CLOSING_STATE = 2;
    // Running status
    public final static int RUNNING_STATE = 3;
    // Stop state
    public final static int STOPPING_STATE = 4;

    // Set the elevator status
    public void setState(int state);

    // Lift action
    public void open(a);
    public void close(a);
    public void run(a);
    public void stop(a);
}

public class Lift implements ILift {
    private int state;

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

    // Close the door
    @Override
    public void close(a) {
        switch (this.state) {
            case OPENING_STATE:
                System.out.println("The elevator is closed...");// The elevator door can be closed only when it is in the open state
                this.setState(CLOSING_STATE);// The elevator is closed after closing
                break;
            case CLOSING_STATE:
                // Do nothing //
                break;
            case RUNNING_STATE:
                // The elevator door is closed when it is running
                break;
            case STOPPING_STATE:
                // The elevator is also closed when it stops
                break; }}// Open the door
    @Override
    public void open(a) {
        switch (this.state) {
            case OPENING_STATE:// The door is already open
                //do nothing
                break;
            case CLOSING_STATE:// The door is closed:
                System.out.println("The elevator doors opened...");
                this.setState(OPENING_STATE);
                break;
            case RUNNING_STATE:
                // The elevator cannot open while it is running
                break;
            case STOPPING_STATE:
                System.out.println("The elevator door opened...");// The elevator has stopped and can open now
                this.setState(OPENING_STATE);
                break; }}// Execute the run action
    @Override
    public void run(a) {
        switch (this.state) {
            case OPENING_STATE:// You can't leave an elevator with the door open
                //do nothing
                break;
            case CLOSING_STATE:// The door is closed and ready to run
                System.out.println("The elevator is running...");
                this.setState(RUNNING_STATE);// Now it is running
                break;
            case RUNNING_STATE:
                //do nothing is already running
                break;
            case STOPPING_STATE:
                System.out.println("The elevator is running...");
                this.setState(RUNNING_STATE);
                break; }}// Perform the stop action
    @Override
    public void stop(a) {
        switch (this.state) {
            case OPENING_STATE: // The elevator is already closed.
                //do nothing
                break;
            case CLOSING_STATE:// Stop when closing the door
                System.out.println("The elevator has stopped...");
                this.setState(STOPPING_STATE);
                break;
            case RUNNING_STATE:// The runtime can of course be stopped
                System.out.println("The elevator has stopped...");
                this.setState(STOPPING_STATE);
                break;
            case STOPPING_STATE:
                //do nothing
                break; }}}public class Client {
    public static void main(String[] args) {
        Lift lift = new Lift();
        lift.setState(ILift.STOPPING_STATE);// The elevator is stopped
        lift.open();/ / open the door
        lift.close();/ / close the door
        lift.run();/ / run
        lift.stop();/ / stop}}Copy the code

Problem analysis

  • Using a lot of switches… If… Else), making the program less readable.
  • Poor scalability. If a new power outage state is added, we need to modify the above judgment logic

25.2.1 Structure of the Status Mode

  • Context role: Also known as Context, it defines the interface required by the client, maintains a current state, and delegates state-related operations to the current state object.
  • Abstract State role: Defines an interface that encapsulates the behavior of a particular State in an environment object.
  • Concrete State role: Implements the behavior corresponding to the abstract State.

25.2.2 Code implementation

Relationship between the class diagram

LiftState

package com.zhuang.state.after;

/ * * *@Classname LiftState
 * @DescriptionAbstract state class *@Date2021/3/31 he, *@Created by dell
 */

public abstract class LiftState {
    // Define an environment role, that is, the functional changes caused by changes in the encapsulation state
    protected Context context;

    public void setContext(Context context) {
        this.context = context;
    }

    // Elevator opening action
    public abstract void open(a);

    // The elevator closes
    public abstract void close(a);

    // Lift operation
    public abstract void run(a);

    // The elevator stops working
    public abstract void stop(a);
}
Copy the code

Context

package com.zhuang.state.after;

/ * * *@Classname Context
 * @DescriptionDefine all elevator door states *@Date 2021/3/31 10:53
 * @Created by dell
 */

public class Context {
    // Define all elevator states
    // The elevator can only be closed
    public final static OpeningState OPENNING_STATE = new OpeningState();
    // In the closed state, the elevator can run, stop and open
    public final static ClosingState CLOSEING_STATE = new ClosingState();
    // When the elevator is running, it can only stop
    public final static RunningState RUNNING_STATE = new RunningState();
    // Stop state, when the elevator can open and run
    public final static StoppingState STOPPING_STATE = new StoppingState();


    // Define a current elevator state
    private LiftState liftState;

    public LiftState getLiftState(a) {
        return this.liftState;
    }

    public void setLiftState(LiftState liftState) {
        // The current environment changes
        this.liftState = liftState;
        // Notify each implementation class of the current environment
        this.liftState.setContext(this);
    }

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

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

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

    public void stop(a) {
        this.liftState.stop(); }}Copy the code

OpeningState

package com.zhuang.state.after;

/ * * *@Classname OpeningState
 * @DescriptionOpen state *@Date2021/3/31 wilt thou *@Created by dell
 */

public class OpeningState extends LiftState {


    // I just want to test the elevator door switch function
    @Override
    public void open(a) {
        System.out.println("Elevator door open...");
    }

    @Override
    public void close(a) {
        // Status changes
        super.context.setLiftState(Context.CLOSEING_STATE);
        // The action delegate is executed with CloseState, that is, the ClosingState subclass is delegated to perform the action
        super.context.getLiftState().close();

    }

    // Don't run with the elevator door open
    @Override
    public void run(a) {
        //do nothing
    }

    // The door is closed
    @Override
    public void stop(a) {
        //do nothing}}Copy the code

ClosingState

package com.zhuang.state.after;

/ * * *@Classname ClosingState
 * @DescriptionClosed state *@Date 2021/3/31 10:52
 * @Created by dell
 */

public class ClosingState extends LiftState {
    @Override
    // The elevator door is closed, this is the closed state to achieve the action
    public void close(a) {
        System.out.println("Elevator door closed...");
    }

    // The elevator door closes and then opens again
    @Override
    public void open(a) {
        super.context.setLiftState(Context.OPENNING_STATE);
        super.context.open();
    }


    // Run when the elevator door is closed
    @Override
    public void run(a) {
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.run();
    }

    // When the elevator door is closed, I don't press the button
    @Override
    public void stop(a) {
        super.context.setLiftState(Context.STOPPING_STATE);
        super.context.stop(); }}Copy the code

RunningState

package com.zhuang.state.after;

/ * * *@Classname RunningState
 * @DescriptionOperating status *@Date 2021/3/31 10:52
 * @Created by dell
 */

public class RunningState extends LiftState {

    @Override
    public void open(a) {
        // Do nothing
    }

    @Override
    public void close(a) {
        // Do nothing
    }

    @Override
    public void run(a) {
        System.out.println("The elevator is running...");
    }

    @Override
    public void stop(a) {
        / / stop
        super.context.setLiftState(Context.OPENNING_STATE);
        super.context.stop(); }}Copy the code

StoppingState

package com.zhuang.state.after;

/ * * *@Classname StoppingState
 * @DescriptionStopped state *@Date2021/3/31 wilt thou *@Created by dell
 */

public class StoppingState extends LiftState {

    @Override
    public void open(a) {
        // Status changes
        super.context.setLiftState(Context.OPENNING_STATE);
        // The action delegates to CloseState for execution, that is, to the ClosingState subclass for execution
        super.context.getLiftState().open();
    }

    @Override
    public void close(a) {
        // Status changes
        super.context.setLiftState(Context.CLOSEING_STATE);
        // The action delegates to CloseState for execution, that is, to the ClosingState subclass for execution
        super.context.getLiftState().close();
    }

    @Override
    public void run(a) {
        // Status changes
        super.context.setLiftState(Context.RUNNING_STATE);
        // The action delegates to CloseState for execution, that is, to the ClosingState subclass for execution
        super.context.getLiftState().run();
    }

    @Override
    public void stop(a) {
        System.out.println("The elevator has stopped..."); }}Copy the code

Client

package com.zhuang.state.after;

/ * * *@Classname Client
 * @DescriptionState mode test classes *@Date 2021/3/31 10:53
 * @Created by dell
 */

public class Client {
    public static void main(String[] args) {
        // Open the door
        System.out.println("Open state -->");
        Context context1 = new Context();
        context1.setLiftState(new OpeningState());
        context1.open();
        context1.close();
        context1.run();
        context1.stop();

        System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = =");
        // Close the door
        System.out.println("Close state -->");
        Context context2 = new Context();
        context2.setLiftState(new ClosingState());
        context2.open();
        context2.close();
        context2.run();
        context2.stop();

        System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = =");
        // Running status
        System.out.println("Running Status -->");
        Context context3 = new Context();
        context3.setLiftState(new RunningState());
        context3.open();
        context3.close();
        context3.run();
        context3.stop();

        System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = =");
        // Stop state
        System.out.println("Stop state -->");
        Context context4 = new Context();
        context4.setLiftState(newStoppingState()); context4.open(); context4.close(); context4.run(); context4.stop(); }}Copy the code

25.3 Application Scenarios of The Status Mode

  • Consider using state patterns when an object’s behavior depends on its state and it must change its behavior based on state at run time.
  • An operation has a large branch structure, and these branches depend on the state of the object.

Write in the last

  • If my article is useful to you, please give me a click πŸ‘, thank you 😊!
  • If you have any questions, please point them out in the comments section! πŸ’ͺ