# 설명
State pattern은 객체의 상태에 따라서 행동이 정해진다고 가정했을때, 상태를 객체 자체에서 체크(if-else구문)하여 행동을 정하는 것이 아닌, 상태를 객체화하여 필요에 따라서 다르게 행동하도록 행동을 위임하는 디자인 패턴이다. 클래스 전반에서 모든 기능이 상태에 의존적으로 행동된다면, 상태를 분리하여 클래스로써 작용 할 수 있도록하는 것이 유용하다.
## Context
운영체제에서도 등장했던 Context의 개념과 유사하다. 어떠한 상태를 쥐고 있는 객체이다. 이 객체의 상태가 변화함에 따라서, 다른 행위를 하게 될 것이다.
## State
인터페이스로써, 컨텍스트는 이 State에 구현된 handle(행위)를 호출함에 따라서, ConcreteState(구현된상태)에 따른 행위가 적용 된다.
## ConcreteState
행위별로 해야할 행동을 정의한다.
State패턴의 미적용사례와 적용사례를 살펴보며, 어떤 이점을 가지는지 몸소 느껴보자.
# state 패턴 미적용
- 맥북의 전원버튼을 누르면 전원이 ON인경우 OFF로, OFF인 경우 ON으로 변화할 것이다.
- 맥북의 전원버튼에 절전모드기능이 추가되면 어떻게 될까?
- 맥북의 전원버튼에 지문인식기능이 추가되면 어떻게 될까?
- 계속해서 기능이 추가된다면 어떻게 될까?
## 코드
### ON - OFF 기능
상태에 따라서 현재의 state가 바뀌고 기능이 구현된다. if-else분기문으로 구현했다.
// MacBook
public class MacBook {
enum Power {OFF,ON}
Power state;
MacBook(){
state = Power.OFF;
System.out.println("====================");
System.out.println("[전원 OFF] 맥북이 생성 됐습니다.");
System.out.println("====================");
}
private void setSate(Power nextSate){
state = nextSate;
}
public void pushButton(){
if (state == Power.OFF) {
System.out.println("[OFF->ON] 맥북 전원을 킵니다.");
setSate(Power.ON);
} else if (state == Power.ON) {
System.out.println("[ON->OFF] 맥북 전원을 종료합니다");
setSate(Power.OFF);
}
}
}
### SLEEP 모드 추가
// MacBook
enum Power {OFF,ON, SLEEP}
public void pushButton(){
if (state == Power.OFF) {
System.out.println("[OFF->ON] 맥북이 시작합니다.");
setSate(Power.ON);
} else if (state == Power.ON) {
System.out.println("[ON->SLEEP] 맥북이 절전모드에 돌입합니다.");
setSate(Power.SLEEP);
} else if (state == Power.SLEEP) {
System.out.println("[SLEEP->OFF] 맥북 전원이 종료됩니다.");
setSate(Power.OFF);
}
}
### TEST
public class Test {
public static void main(String[] args) {
MacBook mac = new MacBook();
mac.pushButton();
mac.pushButton();
mac.pushButton();
mac.pushButton();
mac.pushButton();
mac.pushButton();
mac.pushButton();
}
}
당연히 전원버튼이 OFF-> ON -> SLEEP -> OFF의 상태변경이 잘 일어나는 것을 볼 수 있다. 그러나 기능을 추가해야 할때 MacBook클래스를 수정해야하는 문제점(OCP:개방폐쇄원칙 준수X)이 발생한다. 상태가 많아질수록 조건문이 늘어나 코드가 복잡해보이고, 여러코드에서 중복해서 코드가 쓰일 수 있는 문제점이 있다.
State를 하나의 클래스로 만들어 이 문제를 해결해 보자.
# state 패턴 적용
## 코드 ON - OFF
### Context
// MacBook
public class MacBook {
PowerState state;
MacBook(){
setSate(new OffState());
System.out.println("====================");
System.out.println("[전원 OFF] 맥북이 생성 됐습니다.");
System.out.println("====================");
}
public void setSate(PowerState nextSate){
state = nextSate;
}
public void pushButton(){
state.pushButton(this);
}
}
### State
// PowerState
public interface PowerState {
public void pushButton(MacBook mac);
}
// OnState
public class OnState implements PowerState {
@Override
public void pushButton(MacBook mac) {
System.out.println("[ON->OFF] 맥북이 종료됩니다.");
mac.setSate(new OffState());
}
}
// OffState
public class OffState implements PowerState {
@Override
public void pushButton(MacBook mac) {
System.out.println("[OFF->ON] 맥북이 시작합니다.");
mac.setSate(new OnState());
}
}
## SLEEP 추가
### Context
State 패턴을 적용했기 때문에 Context에서는 코드 수정이 필요가 없다.
### State
// SleepState
public class SleepState implements PowerState{
@Override
public void pushButton(MacBook mac) {
System.out.println("[SLEEP->OFF] 맥북이 종료됩니다.");
mac.setSate(new OffState());
}
}
Sleep모드에 추가에 따른 코드 변경이 필요한 OnState만 수정해주면 된다.
// OnState
public class OnState implements PowerState {
@Override
public void pushButton(MacBook mac) {
System.out.println("[ON->SLEEP] 맥북이 절전모드에 돌입합니다.");
mac.setSate(new SleepState());
}
}
### Test
테스트코드는 똑같으며, 결과 역시 똑같은걸 알 수 있다.
# 마치며
상태패턴과 전략패턴의 코드 짜임의 형태가 유사하다. 그러나 쓰임의 목적이 다르다. 전략패턴은 생성된 전략(알고리즘)을 때에 맞게 변경해주는 역할이며, 상태패턴은 상태를 바꾸어 행위를 바꾸어 주는 역할을 한다.
상태패턴은 상태가 유기적으로 작용해야하기 때문에, 상태를 스스로 변환해줄 수 있다.
그러나 전략패턴은 client가 어떠한 전략으로 변경을 필요할때 직접 변경을 해준다는 것이다.
# 참고
'• 독서 > Design Pattern' 카테고리의 다른 글
[디자인 패턴] 메멘토 패턴(Memento Pattern) (0) | 2022.10.10 |
---|---|
[디자인 패턴] 옵저버 패턴(Observer Pattern) (1) | 2022.10.04 |
[디자인패턴] 어댑터 패턴(Adapter Pattern) (0) | 2022.10.01 |
[디자인 패턴] 컴포지트 패턴(Composite Pattern) (0) | 2022.09.30 |
[디자인패턴] 데코레이터 패턴(Decorator Pattern) (0) | 2022.09.27 |