Understand the State pattern by comparing JavaScript and Java code

Introduction

Details and other patterns will be written in ** Understanding design patterns by comparing implementations in JavaScript and Java **. I wrote an example of JavaScript by looking at Java. We do not take advantage of differences in features such as class type / prototype type, typed strength, and access modifiers. Please note.

State pattern

Expressing "state" as a class state means "state (state of things)"

Implementation example in Java

Consider a vault security system where the security status changes with time

Class diagram

State.png

code

Main.java


public class Main {
    public static void main(String[] args) {
        SafeFrame frame = new SafeFrame("State Sample");
        while (true) {
            for (int hour = 0; hour < 24; hour++) {
                frame.setClock(hour);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {

                }
            }
        }
    }
}

Context.java


public interface Context {
    public abstract void setClock(int hour);
    public abstract void changeState(State state);
    public abstract void callSecurityCenter(String msg);
    public abstract void recordLog(String msg);
}

SafeFrame.java


import java.awt.Frame;
import java.awt.Label;
import java.awt.Color;
import java.awt.Button;
import java.awt.TextField;
import java.awt.TextArea;
import java.awt.Panel;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class SafeFrame extends Frame implements ActionListener, Context {
    private TextField textClock = new TextField(60);
    private TextArea textScreen = new TextArea(10, 60);
    private Button buttonUse = new Button("Safe use");
    private Button buttonAlarm = new Button("emergency bell");
    private Button buttonPhone = new Button("Calls to the office");
    private Button buttonExit = new Button("End");

    private State state = DayState.getInstance();

    public SafeFrame(String title) {
        super(title);
        setBackground(Color.lightGray);
        setLayout(new BorderLayout());

        add(textClock, BorderLayout.NORTH);
        textClock.setEditable(false);

        add(textScreen, BorderLayout.CENTER);
        textScreen.setEditable(false);

        Panel panel = new Panel();
        panel.add(buttonUse);
        panel.add(buttonAlarm);
        panel.add(buttonPhone);
        panel.add(buttonExit);

        add(panel, BorderLayout.SOUTH);

        pack();
        setVisible(true);

        buttonUse.addActionListener(this);
        buttonAlarm.addActionListener(this);
        buttonPhone.addActionListener(this);
        buttonExit.addActionListener(this);
    } 

    public void actionPerformed(ActionEvent e) {
        System.out.println(e.toString());
        if (e.getSource() == buttonUse) {
            state.doUse(this);
        } else if (e.getSource() == buttonAlarm) {
            state.doAlarm(this);
        } else if (e.getSource() == buttonPhone) {
            state.doPhone(this);
        } else if (e.getSource() == buttonExit) {
            System.exit(0);
        } else {
            System.out.println("?");
        }
    }

    public void setClock(int hour) {
        String clockstring = "The current time is";

        if (hour < 10) {
            clockstring += "0" + hour + ":00";
        } else {
            clockstring += hour + ":00";
        }
        System.out.println(clockstring);
        textClock.setText(clockstring);
        state.doClock(this, hour);
    }

    public void changeState(State state) {
        System.out.println(this.state + "From" + state + "The state has changed.");
        this.state = state;
    }

    public void callSecurityCenter(String msg) {
        textScreen.append("call! " + msg + "\n");
    }

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

State.java


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);
}

DayState.java


public class DayState implements State {
    private static DayState singleton = new DayState();
    
    private DayState() {

    }
    public static State getInstance() {
        return singleton;
    }
    public void doClock(Context context, int hour) {
        if (hour < 9 || 17 <= hour) {
            context.changeState(NightState.getInstance());
        }
    }
    public void doUse(Context context) {
        context.recordLog("Safe use(Daytime)");
    }
    public void doAlarm(Context context) {
        context.callSecurityCenter("emergency bell(Daytime)");
    }
    public void doPhone(Context context) {
        context.callSecurityCenter("Normal call(Daytime)");
    }
    public String toString() {
        return "[Daytime]";
    }
}

NightState.java


public class NightState implements State {
    private static NightState singleton = new NightState();

    private NightState() {

    }
    public static State getInstance() {
        return singleton;
    }
    public void doClock(Context context, int hour) {
        if (9 <= hour && hour < 17) {
            context.changeState(DayState.getInstance());
        }
    }
    public void doUse(Context context) {
        context.callSecurityCenter("Emergency: Use the safe at night!");
    }
    public void doAlarm(Context context) {
        context.callSecurityCenter("emergency bell(Night)");
    }
    public void doPhone(Context context) {
        context.recordLog("Night call recording");
    }
    public String toString() {
        return "[Night]";
    }
}

Implementation example in JavaScript

code

index.html


<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>State pattern</title>
	<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="main container flex">
	<input type="text" name="output" disabled="disabled">
	<textarea disabled="disabled"></textarea>
	<div class="button_area flex">
		<button>Safe use</button>
		<button>emergency bell</button>
		<button>Regular phone</button>
		<button>End</button>
	</div>
</div>
<script src="Main.js"></script>
<script src="Context.js"></script>
<script src="DayState.js"></script>
<script src="NightState.js"></script>
</body>
</html>

style.css


/****************************************************************
common part
****************************************************************/
/*reset*/
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, 
fieldset, input, textarea, p, blockquote, th, td{
    margin: 0;
    padding: 0;
}
html{
}
h1, h2, h3, h4, h5, h6{
    font-size: 100%;
    font-weight: normal;
}
ol, ul{
    list-style:none;
}
fieldset, img{
    border:0;
}
table{
    border-collapse: collapse;
    border-spacing:0;
}
caption, th{
    text-align: left;
}
address, caption, cite, code, dfn, em, strong, th, var{
    font-style: normal;
    font-weight: normal;
}
img {
   vertical-align: bottom;
}
html {
    font-size: 10px;
    font-size: 62.5%;
}
a {
    color: #000;
    text-decoration: none;
}
/*setting*/
body {
    font-size: 10px;
    font-size: 1rem;
    background-color: #dbdbdb;
}
.container {
    width: 490px;
    margin: 0 auto;
}
.flex {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
/*main*/
.main {
	margin-top: 20px;
	flex-direction: column;
}
.main input {
	width: 100%;
	height: 25px;
	border: 1px solid #000;
	box-sizing: border-box;
}
.main textarea {
	width: 100%;
	height: 200px;
	box-sizing: border-box;
	border: 1px solid #000;
	border-top: 0;
    overflow: scroll;
}
.main textarea:focus {
	outline: 0;
	
}
.main .button_area {
	width: 100%;
	border:1px solid #000;
	border-top: 0;
	justify-content: center;
	box-sizing: border-box;
}
.button_area button {
	margin: 5px;
}

Main.js


MAIN = {};
MAIN.context;
MAIN.timer;
MAIN.hour;

MAIN.init = function() {
    MAIN.context = new Context();
    MAIN.hour = 0;
    MAIN.timer = setInterval(MAIN.mainLoop, 500);
};
MAIN.mainLoop = function() {
    MAIN.context.setClock(MAIN.hour);
    MAIN.hour += 1;
    if (MAIN.hour === 25) {
        MAIN.hour = 0;
    }
};

window.addEventListener("load", MAIN.init);

Context.js


var Context = function() {
    this.state = DAY_STATE.dayState.getInstance();
    this.textFieldElm = document.querySelector(".main input[type='text']");
    this.textAreaElm = document.querySelector(".main textarea");

    document.querySelectorAll(".main .button_area button").forEach(function(b) {
        switch (b.innerText) {
            case "Safe use":
                b.addEventListener("click", function() {
                    MAIN.context.state.doUse(MAIN.context);
                });
                break;
            case "emergency bell":
                b.addEventListener("click", function() {
                    MAIN.context.state.doAlarm(MAIN.context);
                });
                break;
            case "Regular phone":
                b.addEventListener("click", function() {
                    MAIN.context.state.doPhone(MAIN.context);
                });
                break;
            case "End":
                b.addEventListener("click", function() {
                    window.close();
                });
                break;
        }
    });
};

Context.prototype = {
    constructor: Context,

    setClock: function(hour) {
        var clockString = "The current time is";

        if (hour < 10) {
            clockString += "0" + hour + ":00";
        } else {
            clockString += hour + ":00";
        }
        console.log(clockString);
        this.textFieldElm.value = clockString;
        this.state.doClock(this, hour);

    },
    changeState: function(state) {
        console.log(this.state.getName() + "From k" + state.getName() + "The state has changed.");
        this.state = state;
    },
    callSecurityCenter: function(msg) {
        this.textAreaElm.value += msg + "\n";
        this.textAreaElm.scrollTop = this.textAreaElm.scrollHeight;
    },
    recordLog: function(msg) {
        this.textAreaElm.value += msg + "\n";
        this.textAreaElm.scrollTop = this.textAreaElm.scrollHeight;
    }
};

DayState.js


DAY_STATE = {};
DAY_STATE.dayState = (function() {
    var singleton;
    var name;

    var init = function() {
        name = "[Daytime]"
        return {
            doClock: function(context, hour) {
                if (hour < 9 || 17 <= hour) {
                    context.changeState(NIGHT_STATE.nightState.getInstance());
                }
            },
            doUse: function(context) {
                context.recordLog("Safe use(Daytime)");
            },
            doAlarm: function(context) {
                context.callSecurityCenter("emergency bell(Daytime)");
            },
            doPhone(context) {
                context.callSecurityCenter("Normal call(Daytime)");
            },
            getName: function() {
                return name;
            }
        };
    };

    return {
        getInstance: function() {
            if (!singleton) {
                singleton = init();
            }

            return singleton;
        }
    }
})();

NightState.js


NIGHT_STATE = {};
NIGHT_STATE.nightState = (function() {
    var singleton;
    var name;

    var init = function() {
        name = "[Night]";
        return {
            doClock: function(context, hour) {
                if (9 <= hour && hour < 17) {
                    context.changeState(DAY_STATE.dayState.getInstance());
                }
            },
            doUse: function(context) {
                context.recordLog("Emergency: Use the safe at night!")
            },
            doAlarm: function(context) {
                context.callSecurityCenter("emergency bell(Night)");
            },
            doPhone: function(context) {
                context.callSecurityCenter("Night call recording");
            },
            getName: function() {
                return name;
            }
        };
    };

    return {
        getInstance: function() {
            if (!singleton) {
                singleton = init();
            }

            return singleton;
        }
    };
})();

Characters in the State pattern

** State role **

Represents the state A collection of methods that behave in a state-dependent manner Sample program ⇒ State (interface)

** The role of Concreate State **

Representing specific individual states Sample program ⇒ DayState (class), NightState (class)

** The role of Context (situation, context, context) **

Represents the current state Sample program ⇒ Context (interface)

State pattern class diagram

State2.png

Need for State pattern

You can also change the movement by seeing the change in the state with an if statement or switch statement.

Sample.java


public void method1(Status) {
  if (State A) {
    System.out.println("Since the state A Hello");
  }
  if (State B) {
    System.out.println("Good evening because it is in state B");
  }
}

public void method2(Status) {
  if (State A) {
    System.out.println("Hello because it is in state A");
  }
  if (State B) {
    System.out.println("Good evening because it is in state B");
  }
}

But programmers have to be aware of the difference in state every time. Many places have to be edited when states are added If the state is expressed as a class, "change of state" can be expressed by switching the class.

qiita1.PNG

Easy to understand because you only have to add the ConcreateState role when adding a new state

qiita2.PNG

When using the State pattern

Related patterns

reference

[Introduction to Design Patterns Learned in the Enhanced and Revised Java Language](https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82% E7% 89% 88Java% E8% A8% 80% E8% AA% 9E% E3% 81% A7% E5% AD% A6% E3% 81% B6% E3% 83% 87% E3% 82% B6% E3% 82% A4% E3% 83% B3% E3% 83% 91% E3% 82% BF% E3% 83% BC% E3% 83% B3% E5% 85% A5% E9% 96% 80-% E7% B5 % 90% E5% 9F% 8E-% E6% B5% A9 / dp / 4797327030)

Recommended Posts

Understand the State pattern by comparing JavaScript and Java code
Understand the Decorator pattern by comparing JavaScript and Java code
Understand the Composite pattern by comparing JavaScript and Java code
Understand design patterns by comparing implementations in JavaScript and Java [Updated from time to time]
Execution by subshell (and variable update)
Notify error and execution completion by LINE [Python]
pytube execution and error
Understand the Decorator pattern by comparing JavaScript and Java code
Understand the State pattern by comparing JavaScript and Java code
Understand the Composite pattern by comparing JavaScript and Java code
The difference between foreground and background processes understood by the principle
Try to implement and understand the segment tree step by step (python)
Read the file by specifying the character code.
Understand the Decision Tree and classify documents
Learn the design pattern "State" in Python
Understand the Decorator pattern by comparing JavaScript and Java code
Understand the State pattern by comparing JavaScript and Java code
Understand the Composite pattern by comparing JavaScript and Java code
Understand design patterns by comparing implementations in JavaScript and Java [Updated from time to time]
Try to implement and understand the segment tree step by step (python)
Java compilation and execution understood by CLI
Read the file by specifying the character code.
Understand the Decision Tree and classify documents
Learn the design pattern "Strategy" in Python