1. 예제 프로그램 - 금고경비 시스템 : 시각마다 경비의 상태가 변화하는 금고경비 시스템
<State.java>
package ch19.Sample;
// 금고의 상태를 나타냄
public interface State {
// 각 메소드의 형식 인자는 Context
// Context 클래스: 상태를 관리하거나 실제 경비센터를 호출하는 일을 하는 클래스
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>
package ch19.Sample;
// 주간의 상태를 나타내는 클래스
public class DayState implements State {
// 하나의 상태만 필요하므로, Singleton 패턴을 사용함
private static DayState singleton = new DayState();
private DayState() { // 생성자는 private
}
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("금고사용(주간)"); // 주간에 금고를 사용했음을 기록
}
// 비상벨로 경비센터를 호출하는 메소드
public void doAlarm(Context context) { // 비상벨
context.callSecurityCenter("비상벨(주간)");
}
// 경비센터에 일반통화를 함
public void doPhone(Context context) {
context.callSecurityCenter("일반 통화(주간)");
}
public String toString() { // 문자열 표현
return "[주간]";
}
}
<NightState.java>
package ch19.Sample;
// 야간의 상태를 나타내는 클래스
public class NightState implements State {
private static NightState singleton = new NightState();
private NightState() { // 생성자는 private
}
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("비상:야간의 금고사용");
}
public void doAlarm(Context context) { // 비상벨
context.callSecurityCenter("비상벨(야간)");
}
public void doPhone(Context context) { // 일반 통화
context.recordLog("야간의 통화 녹음");
}
public String toString() { // 문자열 표현
return "[야간]";
}
}
<Context.java>
package ch19.Sample;
// 상태를 관리하거나 경비센터를 실제로 호출하는 클래스를 위한 인터페이스.
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>
package ch19.Sample;
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;
// GUI를 사용해서, 금고경비 시스템을 실현한다.
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("금고사용"); // 금고사용 버튼
private Button buttonAlarm = new Button("비상벨"); // 비상벨 버튼
private Button buttonPhone = new Button("일반 통화"); // 일반 통화 버튼
private Button buttonExit = new Button("종료"); // 종료 버튼
// 금고의 현재 상태를 저장하는 변수
private State state = DayState.getInstance(); // '주간'으로 초기화 함
// 생성자
public SafeFrame(String title) {
super(title);
setBackground(Color.lightGray); // 배경색의 설정
setLayout(new BorderLayout()); // 레이아웃 매니저의 설정
// textClock을 배치
add(textClock, BorderLayout.NORTH); //
textClock.setEditable(false);
// textScreen을 배치
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();
show();
// 리스너의 설정
buttonUse.addActionListener(this);
buttonAlarm.addActionListener(this);
buttonPhone.addActionListener(this);
buttonExit.addActionListener(this);
}
// 버튼이 눌러지면 이 메소드가 실행된다.
// 눌러진 버튼의 종류에 따라
// state.doUse(this) / state.doAlarm(this) / state.doPhone(this) 중
// 하나를 호출한다.
// => 현재 상태가 주간인지 야간이지 체크하는 코드가 필요없다.
public void actionPerformed(ActionEvent e) {
System.out.println("" + e);
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("?");
}
}
// 시간 설정을 위해서 클라이언트(Main)가 호출하는 메소드
public void setClock(int hour) {
String clockstring = "현재 시각은";
if (hour < 10) { // 현재 시각이 두 자리수가 아니면...
clockstring += "0" + hour + ":00";
} else { // 현재 시각이 두 자리수이면...
clockstring += hour + ":00";
}
System.out.println(clockstring); // 화면에 출력
textClock.setText(clockstring); // GUI에 출력
state.doClock(this, hour); // 현재 상태를 나타내는 객체에 현재 시각을 설정한다.
}
// 현재의 상태를 변화시킬 때 호출하는 메소드
public void changeState(State state) {
System.out.println(this.state + "에서" + state + "로 상태가 변화했습니다.");
this.state = state;
}
// 경비센터에 대한 호출을 표현함
public void callSecurityCenter(String msg) {
textScreen.append("call! " + msg + "\n");
}
// 경비센터의 로그에 기록하는 일을 표현함
public void recordLog(String msg) {
textScreen.append("record ... " + msg + "\n");
}
}
<Main.java>
package ch19.Sample;
//public class Main extends Thread {
public class Main {
public static void main(String[] args) {
SafeFrame frame = new SafeFrame("State Sample");
// 1초 간격으로 SafeFrame의 setClock( ) 메소드를 호출한다.
while (true) { // 무한루프
for (int hour = 0; hour < 24; hour++) {
frame.setClock(hour); // 금고의 시간을 설정한다.
try {
Thread.sleep(1000); // 1초간 쉰다.
} catch (InterruptedException e) {
}
}
}
}
}
2. 연습문제 3번 - "점심시간"이라는 상태를 추가하시오
<State.java>
package ch19.A3;
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>
package ch19.A3;
public class DayState implements State {
private static DayState singleton = new DayState();
private DayState() { // 생성자는 private
}
public static State getInstance() { // 유일한 하나의 인스턴스를 얻는다
return singleton;
}
public void doClock(Context context, int hour) { // 시각설정
if (hour < 9 || 17 <= hour) {
context.changeState(NightState.getInstance());
} else if (12 <= hour && hour < 13) {
context.changeState(NoonState.getInstance());
}
}
public void doUse(Context context) { // 금고사용
context.recordLog("금고사용(주간)");
}
public void doAlarm(Context context) { // 비상벨
context.callSecurityCenter("비상벨(주간)");
}
public void doPhone(Context context) { // 일반 통화
context.callSecurityCenter("일반 통화(주간))");
}
public String toString() { //문자열 표현
return "[주간]";
}
}
<NoonState.java>
package ch19.A3;
public class NoonState implements State {
private static NoonState singleton = new NoonState();
private NoonState() { // 생성자는 private
}
public static State getInstance() { // 유일한 하나의 인스턴스를 얻는다.
return singleton;
}
public void doClock(Context context, int hour) { // 시각 설정
if (hour < 9 || 17 <= hour) {
context.changeState(NightState.getInstance());
} else if (9 <= hour && hour < 12 || 13 <= hour && hour < 17) {
context.changeState(DayState.getInstance());
}
}
public void doUse(Context context) { // 금고 사용
context.callSecurityCenter("비상:점심시간 때의 금고사용!");
}
public void doAlarm(Context context) { // 비상벨
context.callSecurityCenter("비상벨(점심시간 때)");
}
public void doPhone(Context context) { // 일반통화
context.recordLog("점심시간 때의 통화 녹음");
}
public String toString() { // 문자열 표현
return "[점심시간 때]";
}
}
<NightState.java>
package ch19.A3;
public class NightState implements State {
private static NightState singleton = new NightState();
private NightState() { //생성자는 private
}
public static State getInstance() { // 유일한 하나의 인스턴스를 얻는다
return singleton;
}
public void doClock(Context context, int hour) { // 시각설정
if (9 <= hour && hour < 17) {
context.changeState(DayState.getInstance());
} else if (12 <= hour && hour < 13) {
context.changeState(NoonState.getInstance());
}
}
public void doUse(Context context) { // 금고사용
context.callSecurityCenter("비상:야간의 금고사용");
}
public void doAlarm(Context context) { // 비상벨
context.callSecurityCenter("비상벨(야간)");
}
public void doPhone(Context context) { // 일반 통화
context.recordLog("야간의 통화 녹음");
}
public String toString() { // 문자열 표현
return "[야간]";
}
}
<Context.java>
package ch19.A3;
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>
package ch19.A3;
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("금고사용"); // 금고사용 버튼
private Button buttonAlarm = new Button("비상벨"); // 비상벨 버튼
private Button buttonPhone = new Button("일반 통화"); // 일반 통화 버튼
private Button buttonExit = new Button("종료"); // 종료 버튼
private State state = DayState.getInstance(); // 현재의 상태
// 생성자
public SafeFrame(String title) {
super(title);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
// textClock을 배치
add(textClock, BorderLayout.NORTH);
textClock.setEditable(false);
// textScreen을 배치
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();
show();
// 리스너의 설정
buttonUse.addActionListener(this);
buttonAlarm.addActionListener(this);
buttonPhone.addActionListener(this);
buttonExit.addActionListener(this);
}
// 버튼이 눌러지면 여기로 온다.
public void actionPerformed(ActionEvent e) {
System.out.println("" + e);
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 = "현재 시각은";
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 + "에서" + state + "로 상태가 변화했습니다.");
this.state = state;
}
// 경비센터 호출
public void callSecurityCenter(String msg) {
textScreen.append("call! " + msg + "\n");
}
// 경비센터 기록
public void recordLog(String msg) {
textScreen.append("record ... " + msg + "\n");
}
}
<Main.java>
package ch19.A3;
public class Main extends Thread {
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) {
}
}
}
}
}
3. 연습문제 4 - "비상시"라는 상태를 추가하시오
<State.java>
package ch19.A4;
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>
package ch19.A4;
public class DayState implements State {
private static DayState singleton = new DayState();
private DayState() { // 생성자는 private
}
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("금고사용(주간)");
}
public void doAlarm(Context context) { // 비상벨
context.callSecurityCenter("비상벨(주간)");
context.changeState(UrgentState.getInstance());
}
public void doPhone(Context context) { // 일반 통화
context.callSecurityCenter("일반 통화(주간)");
}
public String toString() { // 문자열 표현
return "[주간]";
}
}
<NightState.java>
package ch19.A4;
public class NightState implements State {
private static NightState singleton = new NightState();
private NightState() { // 생성자는 private
}
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("비상:야간의 금고사용");
}
public void doAlarm(Context context) { // 비상벨
context.callSecurityCenter("비상벨(야간)");
context.changeState(UrgentState.getInstance());
}
public void doPhone(Context context) { // 일반 통화
context.recordLog("야간의 통화 녹음");
}
public String toString() { // 문자열 표현
return "[야간]";
}
}
<UrgentState.java>
package ch19.A4;
public class UrgentState implements State {
private static UrgentState singleton = new UrgentState();
private UrgentState() { // 생성자는 private
}
public static State getInstance() { // 유일한 하나의 인스턴스를 얻는다.
return singleton;
}
public void doClock(Context context, int hour) { // 시각설정
// 시각설정으로는 아무것도 처리하지 않는다.
}
public void doUse(Context context) { // 금고사용
context.callSecurityCenter("비상:비상시의 금고사용");
}
public void doAlarm(Context context) { // 비상벨
context.callSecurityCenter("비상벨(비상시)");
}
public void doPhone(Context context) { // 일반 통화
context.callSecurityCenter("일반 통화(비상시)");
}
public String toString() { // 문자열 표현
return "[비상시]";
}
}
<Context.java>
package ch19.A4;
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>
package ch19.A4;
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("금고사용"); // 금고사용 버튼
private Button buttonAlarm = new Button("비상벨"); // 비상벨 버튼
private Button buttonPhone = new Button("일반 통화"); // 일반 통화 버튼
private Button buttonExit = new Button("종료"); // 종료 버튼
private State state = DayState.getInstance(); // 현재의 상태
// 생성자
public SafeFrame(String title) {
super(title);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
// textClock을 배치
add(textClock, BorderLayout.NORTH);
textClock.setEditable(false);
// textScreen을 배치
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();
show();
// 리스너의 설정
buttonUse.addActionListener(this);
buttonAlarm.addActionListener(this);
buttonPhone.addActionListener(this);
buttonExit.addActionListener(this);
}
// 버튼이 눌러지면 여기로 온다.
public void actionPerformed(ActionEvent e) {
System.out.println("" + e);
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 = "현재 시각은";
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 + "에서" + state + "로 상태가 변화했습니다.");
this.state = state;
}
// 경비센터 호출
public void callSecurityCenter(String msg) {
textScreen.append("call! " + msg + "\n");
}
// 경비센터 기록
public void recordLog(String msg) {
textScreen.append("record ... " + msg + "\n");
}
}
<Main.java>
package ch19.A4;
public class Main extends Thread {
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) {
}
}
}
}
}