프로그래밍/자바 디자인패턴

디자인패턴 State 패턴

가카리 2012. 8. 14. 22:47
반응형

State 패턴

 

이름

해설

State

금고의 상태를 나타내는 인터페이스

DayState

State를 구현하고 있는 클래스, 주간의 상태를 나타낸다.

NightState

State를 구현하고 있는 클래스, 야간의 상태를 나타낸다.

Context

금고의 상태변환를 관리하고 경비센터와 연락을 취하는 인터페이스

SafeFrame

Context를 구현하는 클래스, 버튼이나 화면표시 등의 사용자인터페이스를 갖는다.

Main

동작 테스트용 클래스

 

 

 

 

 

 

State 인터페이스

 

package kch;

 

 

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 클래스

 

package kch;

 

public class DayState implements State {

 

           private static DayState singleton = new DayState();

           //주간 상태는 하나만 인스턴스를 만들기 위해서

           //singleton 패턴을 씀.

          

           //그래서 생성자는 private

           private DayState(){

                    

           }

          

           public static State getInstance(){//유일한 인스턴스를 얻는다.

                     return singleton;

           }

          

           @Override

           public void doClock(Context context, int hour) {

                     // TODO Auto-generated method stub

                     if(hour < 9 || 17 <= hour){//이 시간때는 저녁시간이므로.

                                context.changeState(NightState.getInstance());//state 바꾸기위해서 넘겨줌.

                     }

           }

 

           @Override

           public void doUse(Context context) {

                     // TODO Auto-generated method stub

                     context.recordLog("금고사용(주간)");

           }

 

           @Override

           public void doAlarm(Context context) {

                     // TODO Auto-generated method stub

                     context.callSecurityCenter("비상벨 (주간)");

           }

 

           @Override

           public void doPhone(Context context) {

                     // TODO Auto-generated method stub

                     context.callSecurityCenter("일반통화(주간)");

           }

           public String toString(){

                     return "[주간]";

           }

          

}

 

 

NightState 클래스

 

package kch;

 

 

public class NightState implements State{

 

           private static NightState singleton = new NightState();

          

           //인스턴스를 하나만 생성하기 위해서 singleton 패턴을 씀

           //그래서 생성자는 private

           private NightState(){

                    

           }

          

           public static State getInstance(){//유일한 인스턴스를 얻는다.

                     return singleton;

           }

          

           @Override

           public void doClock(Context context, int hour) {//시간설정

                     // TODO Auto-generated method stub

                     if(9 <= hour && hour < 17){//이시간대면 낮시간이므로 바꿔줌.

                                context.changeState(DayState.getInstance());

                     }

           }

 

           @Override

           public void doUse(Context context) {//금고사용

                     // TODO Auto-generated method stub

                     context.callSecurityCenter("비상  : 야간금고 사용!");

           }

 

           @Override

           public void doAlarm(Context context) {//비상벨

                     // TODO Auto-generated method stub

                     context.callSecurityCenter("비상벨 (야간)");

           }

 

           @Override

           public void doPhone(Context context) {

                     // TODO Auto-generated method stub

                     context.recordLog("야간통화 녹음");

           }

 

           public String toString(){

                     return "[야간]";

           }

}

 

 

Context 인터페이스

 

package kch;

 

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 클래스

 

package kch;

 

import java.awt.BorderLayout;

import java.awt.Button;

import java.awt.Color;

import java.awt.Frame;

import java.awt.Panel;

import java.awt.TextArea;

import java.awt.TextField;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

 

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

                    

                     //listener 설정

                     buttonUse.addActionListener(this);

                     buttonAlarm.addActionListener(this);

                     buttonPhone.addActionListener(this);

                     buttonExit.addActionListener(this);

                    

           }

 

           //현재시간을 console창과 TextArea에 출력

           @Override

           public void setClock(int hour) {

                     // TODO Auto-generated method stub

                    

                     String clockstring = "현재 시간은";

                     if(hour < 10){

                                clockstring += "0" + hour + ":00";

                     }else{ 

                                clockstring += hour + ":00";

                     }

                    

                     System.out.println(clockstring);

                     textClock.setText(clockstring);//텍스트를 바꿔줌.

                     state.doClock(this, hour);//지금 시간과 SafeFrame 자신을 넘겨줌. 그래서 state 바꿀지말지 결정

                    

           }

 

          

           //상태 전환 현상태를 state 전환함.

           @Override

           public void changeState(State state) {

                     // TODO Auto-generated method stub

                    

                     System.out.println(this.state + "에서" + state + "로 상태가 변화했습니다.");

                     this.state = state;

                    

           }

 

           @Override

           public void callSecurityCenter(String msg) {

                     // TODO Auto-generated method stub

                    

                     textScreen.append("call! " + msg + "\n" );

                    

           }

 

           @Override

           public void recordLog(String msg) {

                     // TODO Auto-generated method stub

                    

                     textScreen.append("record .. " + msg + "\n");

                    

           }

 

          

           //버튼이 눌려지면 이쪽으로 온다.

           @Override

           public void actionPerformed(ActionEvent e) {

                     // TODO Auto-generated method stub

                    

                     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("?");

                               

                     }

          

           }

          

          

          

}

 

 

Main 클래스

 

package kch;

 

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

                                                    

                                          }

                                }

                     }//while

                    

           }

}

 

 

 

 

 

낮과 밤이라는 상태를 분할을 해서 생각해보았습니다. 이렇게 생각을 안한다면

if(주간){

           경비센터에 기록

}else if (야간){

           경비센터에 비상사태 보고

}

이런식으로 if문을 써서 모든 경우의 수를 써야합니다. 상태가 2개가 아니라 수십개라면 이렇게 코딩하면 속도면이나 유지보수 측면에서 상당한 어려움을 겪을 것입니다.

이 코드의 핵심은 SafeFrame 클래스의 public void setClock(int hour)  부분이며

여기서 state.doClock(this, hour); 를 호출해서 지금 상태가 변해야되는지 말아야하는지를 결정합니다.

 

상태가 변해야된다면 context.changeState(NightState.getInstance()); 로 상태를 변화시킵니다.