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.
Expressing "state" as a class state means "state (state of things)"
Consider a vault security system where the security status changes with time
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]";
}
}
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;
}
};
})();
Represents the state A collection of methods that behave in a state-dependent manner Sample program ⇒ State (interface)
Representing specific individual states Sample program ⇒ DayState (class), NightState (class)
Represents the current state Sample program ⇒ Context (interface)
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.
Easy to understand because you only have to add the ConcreateState role when adding a new state
[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