본문 바로가기

Design Pattern

[디자인패턴] State Pattern (스테이트 패턴)

  • 스테이트 패턴의 목적과 사용이유
  • States와 Actions의 다이어그램
  • 스테이트 패턴을 적용시키기 전
  • 스테이트 패턴을 통한 문제 해결
  • 스테이트 패턴 vs 스트래티지 패턴
  • 의문점
  • Related Patterns

 

  • 스테이트 패턴의 목적과 사용이유

Purpose

    - 객체 내부 상태가 바뀜에 따라 객체의 행동을 바꿀 수 있다.

Use When

    - 객체의 행동이 상태로부터 영향 받아야 할 때

    - 복잡한 조건들이 객체의 행동을 상태에 연결지을때

    - 여러 state들 사이에서의 전이가 뚜렷해져야할 때

 

 

 


 

  • States와 Actions의 다이어그램

상태 다이어그램(Gumball Machine)

위는 Gumball 머신에서의 상태, 행동 다이어그램이다. Gumball 머신에서의 상태와 행동을 나열하면 다음과 같다.

 

상태

(1) 동전 없음 (No Quarter)

(2) 동전 있음 (Has Quarter)

(3) Gumball 판매 (Gumball Sold)

(4) Gumball 매진 (Out of Gumballs)

 

행동

(1) 동전 투입 (Insert quarter)

(2) 동전 반환 (ejects quarter)

(3) 손잡이 돌림 (turns crank)

(4) Gumball 내보냄 (dispense gumball)

    - (4-1) gumball을 내보낸 후 모두 소진됐다면 Gumball 매진 상태로 전환

    - (4-2) gumball을 내보낸 후 잔여 gumball이 있다면 동전 없음 상태로 전환

 

 

 


 

  • 스테이트 패턴을 적용시키기 전

GumballMachine

 Gumball 머신은 같은 행동(메소드)이라도 상태에 따라 다른 행동을 취해야한다.

위처럼 if문을 이용한 구현을 한다면 메소드를 호출할때마다 현재의 state들을 확인하면서 각기 다른 행동을 취하도록 해줘야한다. 결국, Gumball 머신에서는 4개의 행동(메소드)이 존재하고 각 행동을 할때마다 4개의 state와 if문 검사를 해야한다.

이때 행동 혹은 상태가 늘어날때마다 수많은 코드의 수정이 필요해진다. OCP 위반!! (by 수많은 if문)

 

 

New Idea!!

 

  • 바뀌는 부분들을 캡슐화해라! (OO principle) : 각 state에서 이뤄져야하는 행동은 그 state안에 둬라. 따라서 state가 바껴도 그냥 state에서의 행동을 호출하여 수행하면 된다. 즉, 현재 상태에 따른 행동은 Gumball 머신이 state object에게 위임하도록(delegate) 해라.
  • 상속보다는 Composition! (?)

 

New Design!!

 

  • Gumball 머신에서의 모든 행동을 State 인터페이스에 정의한다.
  • Gumball 머신에서의 모든 State 클래스를 구현한다.
  • State object에게 행동을 위임함으로써 존재하던 모든 조건 검사문(conditional code)을 지운다.

 

 

 


 

  • 스테이트 패턴을 통한 문제 해결

State Pattern

 상태에 따라 모두 다른 행동을 해야하므로 각기 다르게 Interface내의 행동을을 오버라이딩 해준다.

NoQuarterState

 NoQuarterState에서는 4개의 행동에 대해 위와같이 행동하도록 구현하였다. 오직 동전을 넣는 행동에 대해서만 반응을 한다. NoQuarter상태에서 동전 넣는 행동이 호출되면 gumball 머신의 상태를 HasQuarter 상태로 바꿔준다. 

GumballMachine
GumballMachine

 Gumball 머신에서는 4개의 상태 object를 만들어놓고 현재 상태를 참조하는 변수를 하나 갖고있다.

따라서 행동이 호출되면 현재 상태가 가리키는 state object에서의 행동이 호출되고 state object 역시 new ....State(this) 에 의해 이 gumball 머신을 참조하도록 돼있기 때문에 gumball 머신의 상태를 변화시킬 수 있다. 이를 간략화하면, 

1. Gumball Machine에서 4개의 State object 생성, 현재 state 초기화 - new ....State(this) / state = NoQuarterState

2. Gumball Machine에서 특정 행동(메소드) 호출 - insertQuarter() = state.insertQuarter()

3. NoQuarterState의 insertQuarter()가 호출, Gumball Machine의 state 전이 - gM.setState(gM.hetHasQuarterState())

 

 


 

  • 스테이트 패턴 vs 스트래티지 패턴
  • 두 패턴 모두 Composition 과 Delegation을 이용한다. 그러나 목적이 다르다.
  • 스테이트 패턴은 상태에 따른 행동을 캡슐화한다. 행동이 상태에 따라 run-time에 바뀌고 run-time에서의 if 조건검사를 없애기 위해 스테이트 패턴을 사용한다.
  • 스트래티지 패턴은 알고리즘(행동)을 캡슐화하여 client가 직접 run-time에 쉽게 선택 및 수정하는데 유연성을 주기위한 패턴이다. 

 

 

 


 

  • 의문점
  • 상태 전이하는 작업은 어디서 이루어져야 하는가? : 간단한 상황에서는 Context class에서 해도되지만 ConcreteState class에서 이루어지는게 더 유연성을 제공한다.
  • ConcreteState 객체는 언제 생성돼야 하는가? : 필요할때가 되면 생성하든지, 혹은 Context에서 한번에 생성해놓고 필요할때 참조하도록 할 수 있다.

 

 

 


 

  • Related Patterns
  • State Pattern : 상태에 기반한 행동들을 캡슐화하여 현재 상태에 따라 다른 행동을 하도록 한다.
  • Strategy Pattern : 마찬가지로 Composition과 Delegation을 이용하여 Client가 run-time에 하고싶은 행동을 선택하여 할 수 있도록 한다.
  • Template method : 하위클래스에서 실제로 어떻게 구현할지 steps들을 delegate 받아서 구현한다.