On State mode

Keywords: Programming Java REST React

I. Preface

State mode is very convenient to use in some situations. What is state? If you have learned the compilation principle, you will understand DFA M and NFA M. in determining finite state machines and non deterministic finite state machines, state is the smallest unit. When certain conditions are met, state will change. We can treat a moment in time as a state. State, in fact, the whole society is composed of states. From the previous moment to the next moment, what kind of changes have taken place in the material (space) of the whole society, so the state can be very large or very small. The weather change is state, day and night are state, people's life and rest are state, so the state is everywhere. So the state pattern is to regard a state as a class, which is different from our previous understanding of the class. In the past, we thought that the class is an abstraction of the object, which is used to represent the object, and the object is generally a concrete thing. Now we regard the non concrete invisible but real thing as the thing described by the class, which may need our understanding.

Why do we need state mode? Can we do without state mode? Of course, but it still goes back to the level of code maintainability, scalability and reusability. For example, in this example, we consider a banking system that can be used to withdraw money, make phone calls, alarm and record these four functions, but we consider the following requirements: if it is normal for us to withdraw money in the daytime, an alarm will be sent at night to withdraw money; In the daytime, the phone is answered, and in the evening, the message function is activated; in the daytime and in the evening, the alarm will be triggered by pressing the alarm bell. So how should we design this program? Of course, we can judge whether it is day or night for each action (as a function). Then we can react according to the specific situation. Of course, this is OK, but if we have many states (day and night), such as 24 hours divided into 24 time periods (24 states), then we have to judge every function 24 times, which is undoubtedly very bad code, very poor readability, and if the requirements change, it is very difficult for us to modify the code (it is easy to make mistakes However, if we consider these states as a class, we can process, judge and switch them within each class, so that the idea is very clear. If we add another state, there will be very few changes in the code, which is very convenient for the situation with more states.

Two, code

Context interface:

package zyr.dp.state;

public interface Context {

    public abstract void setClock(int hour);
    public abstract void changeState(State state);
    public abstract void callSecurity(String str);
    public abstract void recordLog(String msg);
    
}

SafeFrame implementation class:

package zyr.dp.state;

import java.awt.*;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class SafeFrame extends Frame implements Context,ActionListener {

    private static final long serialVersionUID = 1676660221139225498L;
    
    private Button btnUse=new Button("Use");
    private Button btnAlarm=new Button("Alarm bell");
    private Button btnPhone=new Button("Phone");
    private Button btnExit=new Button("Sign out");
    
    private TextField tfClock=new TextField(60);
    private TextArea taAlarm=new TextArea(10,60);
    
    private State state=DayState.getInstance();
    
    public SafeFrame(String title){
        super(title);
        setBackground(Color.BLUE);
        setLayout(new BorderLayout());
        
        add(tfClock,BorderLayout.NORTH);
        tfClock.setEditable(false);
        add(taAlarm,BorderLayout.CENTER);
        taAlarm.setEditable(false);
        
        Panel panel=new Panel();
        panel.add(btnUse);
        panel.add(btnAlarm);
        panel.add(btnPhone);
        panel.add(btnExit);
        add(panel,BorderLayout.SOUTH);
        
        pack();
        show();
        
        btnUse.addActionListener(this);
        btnAlarm.addActionListener(this);
        btnPhone.addActionListener(this);
        btnExit.addActionListener(this);
    }
    
    public void setClock(int hour) {
        tfClock.setText(hour<10 ? "Now it's time:" + "0"+hour : "Now it's time:" +hour);
        state.doClock(this, hour);
    }

    public void changeState(State state) {
        System.out.println("Slave state"+this.state+"Changed to"+state);
        this.state=state;
    }

    public void callSecurity(String str) {
        taAlarm.append("Call..."+str+"\n");
    }

    public void recordLog(String msg) {
        taAlarm.append("record..."+msg+"\n");
    }

    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==btnUse){
            state.doUse(this);
        }else if(e.getSource()==btnAlarm){
            state.doAlarm(this);
        }else if(e.getSource()==btnPhone){
            state.doPhone(this);
        }else if(e.getSource()==btnExit){
            System.exit(0);
        }else{
            System.out.print("Unexpected error!");
        }
    }

}

State interface:

package zyr.dp.state;

public interface State {

    public abstract void doClock(Context context,int hour);
    public abstract void doUse(Context context);
    public abstract void doAlarm(Context context);
    public abstract void doPhone(Context context);
    
}

NightState implementation class:

package zyr.dp.state;

public class NightState implements State {

    private NightState(){
        
    }
    private static NightState nightState=new NightState();

    public static NightState getInstance() {
        return nightState;
    }

    public void doClock(Context context, int hour) {
        if(hour>=6 && hour <18){
            //Day
            context.changeState(DayState.getInstance());
        }
    }

    public void doUse(Context context) {
        context.callSecurity("Evening use");
    }

    public void doAlarm(Context context) {
        context.callSecurity("Night alarm bell");
    }

    public void doPhone(Context context) {
        context.recordLog("Call in the evening");
    }
    
}

DayState implementation class:

package zyr.dp.state;

public class DayState implements State {

    private DayState(){
        
    }
    private static DayState dayState=new DayState();

    public static DayState getInstance() {
        return dayState;
    }

    public void doClock(Context context, int hour) {
        if(hour<6 || hour >=18){
            //Night
            context.changeState(NightState.getInstance());
        }
    }

    public void doUse(Context context) {
        context.callSecurity("Daytime use");
    }

    public void doAlarm(Context context) {
        context.callSecurity("Daytime alarm bell");
    }

    public void doPhone(Context context) {
        context.recordLog("Call during the day");
    }
}

Class Main:

package zyr.dp.state;

public class Main {

    public static void main(String[] args) {

        SafeFrame f=new SafeFrame("State mode");
        while(true){
            for(int hour=1;hour<=24;hour++){
                f.setClock(hour);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }

}

Operation result:

Three, summary

It can be seen that the powerful power of state mode is to abstract the state into a class by the most concise code through interfaces, abstract classes, ordinary classes, inheritance, delegation, agent modes, etc., and then delegate different states to do different things through the logic of control state, and delegate the logic of control state to make corresponding actions and modifications for each state again, which It seems complicated. In fact, if you read it carefully, you will find that because of the interface, the program is very simple, and each state has a clear division of labor and close cooperation.

But state mode also has some disadvantages. Just because each state closely cooperates with each other, we need to know the objects of other states in one state, which results in a certain correlation. There is a close coupling relationship between state and state, which is a disadvantage of state mode. For this reason, we can hand over the code of state migration to SafeFrame to do so. The Mediator arbiter mode is about to be used.

The reason for using singletons is that creating new objects all the time wastes memory, because singletons are enough. The same use of state mode uses state variables to represent the corresponding state through the interface, without confusion and contradiction. Compared with using multiple variables to represent the state between partitions, it is very clear and concise. State mode is convenient for adding new states (it also needs to modify the state migration code of other states), and is not convenient for adding new "state dependent processing", such as doAlarm, etc., because once added, all States implementing the state interface need to add this part of code.

At the same time, we also see the multifaceted nature of the instance. For example, if the SafeFrame instance implements the ActionListener interface and the Context interface, then the new SafeFrame() object can be passed into the fun1(ActionListener a) and fun2(Context context) methods. After that, the two methods use the object differently and have different permissions, so the multiple interfaces will produce multifaceted nature. In fact, the state pattern uses the idea of divide and rule to discuss different states separately and extract commonality, so that the problem becomes simple.

 

On the design mode

Posted by torrentmasta on Fri, 25 Oct 2019 02:00:42 -0700