Chapter 17. State : 상태를 클래스로 표현한다

sua·2022년 4월 9일
0
post-thumbnail

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

profile
가보자고

0개의 댓글

관련 채용 정보