20. The State Pattern
State패턴은 개발자가 객체를 애플리케이션의 상태를 표현하게 할 수 있도록 하며,객체의 상태를 전환함으로써 애플리케이션의 상태도 전환할 수 있게 한다.예를 들어,다수의 관련된 내장 클래스 사이에서 전환하도록 동봉(enclosing)클래스를 설정하거나 현재의 내장 클래스의 메소드 호출을 전달하도록 한다.디자인 패턴에서는state패턴이 동봉된 객체가 해당 클래스를 변경하는 것처럼 보이는 방식으로 내부 클래스 사이를state패턴이 전환하도록 권장한다.적어도 자바에서는 이 방법이 약간 과장되었지만,어느 클래스가 사용되는지에 대한 실제적인 목적이 분명한 변화를 초래할 수 있다.
많은 개발자들이 미묘하게 다른 연산을 수행하거나 클래스에 전달되는 아규먼트에 따라 다양한 정보를 출력하는 클래스를 생성해 본 경험을 갖고 있다.이런 경험을 종종 특정 유형의 전환 작업 및 어느 작업을 수행할지 결정하는 클래스 내부의if-else문을 이끌어내기도 한다.이런한 비효율성이 바로state패턴을 대체해야 하는 이유이다.
구조
|
역할
· Context :클라이언트들이 원하는 인터페이스를 정의한다.현재 상태를 정의하는ConcreteState서브클래스의 인스턴스를 가진다.
· State :Context의 특정 상태와 관련된 행위들을 캡슐화 하기 위한 관련 인터페이스를 정의한다.
· ConcreteState:각각의 서브클래스들은Context의 상태들과 관련된 행위들을 구현한다.
의도
객체의 내부 상태가 바뀌었을 때 객체의 행동을 바꿀 객체를 허용한다.객체는 마치 객체의 클래스가 변경된 것처럼 보일 것이다.
적용시기
- 객체의 행위가 객체의 상태에 의존적일때.그리고 객체가run-time시에 상태에 따라 행위를 바꾸어야 할 경우.
- 객체의 상태에 대한 처리를 위해 구현하는 다중 조건 제어문이 거대해질 경우.이 상태들을 일반적으로 하나나 그 이상의 열거형 상수들로 표현된다.종종 여러 명령들은 객체 상태에 따른 처리를 위해 비슷한 유형의 조건 제어와 관련한 코드를 가지게 된다. State패턴은 각각의 조건분기점들을 클래스로 분리시킨다.이는 객체의 상태들을 다른 객체로부터 다양하게 독립적일 수 있는,고유의 권리를 가지는 객체로서 취급하도록 해준다.
결론
· State패턴은 애플리케이션이 보유하게 될 각각의 기본state정보별 기본state객체의 서브 클래스를 생성하고,해당 클래스들 간의 작업을 애플리케이션이 상세 정보를 변경할 때마다 수행한다.
· 각각의State정보는 클래스 안에 캡슐화되어 있기 때문에 다양한State정보와 관련된 다수의if및switch조건문을 포함하지 않아도 된다.
· 프로그램이 어떤 상태값으로 설정되어 있는지 구체적으로 명시하는 변수는 없다.그러므로 이런 방식은 해당State변수의 확인 작업을 생략한 개발자에 의해 발생하는 오류를 감소시킨다.
· 별도의 창과 같은 애플리케이션의 각 요소 간의State객체들을 어떤 종류의State객체도 구체적으로 인스턴스 변수를 갖지 않는 한 공유할 수 있다.
· 자바 언어에서 모든State객체는 공통 기본 클래스에서 상속되어야 하며,모든 객체는 공통의 메소드를 보유해야 한다.이런 조건은 해당 메소드 중 일부가 빈 공간으로 남아도 지켜줘야 한다.다른 개발 언어에서State패턴은 함수 포인터가 구현하는데,형 검사 작업의 횟수가 크게 감소하는 문제 때문에 오류 발생 빈도가 높아진다.
예제소스
|
예제 소스
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); } 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); } 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("금고사용(주간)"); } public void doAlarm(Context context) { context.callSecurityCenter("비상벨(주간)"); } public void doPhone(Context context) { context.callSecurityCenter("일반 통화(주간)"); } public String toString() { return "[주간]"; } } 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("비상:야간의 금고사용"); } public void doAlarm(Context context) { context.callSecurityCenter("비상벨(야간)"); } public void doPhone(Context context) { context.recordLog("야간의 통화 녹음"); } public String toString() { return "[야간]"; } } 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()); 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(); 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"); } } public class mainClass 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) { } } } } } |
관련패턴
l Singleton패턴: ConcreteState역할은Singleton패턴으로 구현되는 경우가 있다.
l Flyweight패턴:상태를 나타내는 클래스는 인스턴스 필드를 가지지 않는다.따라서Flyweight패턴을 사용해서ConcreteState역할을 다수의Context역할에서 공유할 수 있는 경우도 있다.
'Programming > Design Pattern' 카테고리의 다른 글
[펌] The Observer Pattern (0) | 2006.01.21 |
---|---|
[펌] The Observer Pattern (0) | 2006.01.21 |
[펌] The Template Pattern (0) | 2006.01.21 |
[펌] The Visitor Pattern (0) | 2006.01.21 |
[펌] The Interpreter Pattern (0) | 2006.01.21 |