쫑쫑쫑:[state pattern]
http://blog.naver.com/setimets/30049303269
사실 그 설명 안에 이미 상태패턴이 뭘하는 건지에 대한 답은 충분히 있다고 할 수 있겠습니다.
상태패턴의 중요한 특징은 전이라는건데, 대체 전이가 뭘까요?(참고로 전이를 하지 않으면 상태패턴이 아니랍니다. 전략패턴이죠!)
전이는 영어로는 transition 이라고 합니다. 보통 화면의 장면전환시에 트랜지션효과를 준다라고 하는데 그때와 같은 단어입니다.
전이란 현재의 어떤 주어진 목표를 수행하던 actor를 수행할 목표는 변하지 않은 상태로 actor만 교체하는 일을 말합니다.
즉 눈앞에 무거운 짐을 200미터 옮겨야하는데, 처음 100미터는 제가 옮기다가 중간에 종희로 바꿔서 다시 100미터를 옮긴 경우
1. 원래 목표인 200미터 옮기기는 변하지 않은 상태로
2. actor인 저와 종희만 교체했습니다.
이 때 교체한 행위가 바로 전이라는 것입니다.
전이의 기초 개념이 생겼으니 좀더 생각해봅시다. 전이의 핵심은 ‘actor교체’인데 이때 중요한 문제가 있습니다. 전이라는 행위를 누가 할까라는거죠.
1.
만약 처음부터 200미터를 옮기라고 지시하는 작업반장이 있는 경우를 생각해봅시다. 이 경우엔 작업반장이 상황을 보거나 지 기분에 따라 actor를 교체합니다. ‘야 거기, 종희로 바꿔!’ 라고 하는 거죠. 이걸 디자인패턴에서는 뭐라고 하나요?
이것이 바로 전략패턴입니다. actor전이를 호스트가 하는 패턴이 바로 전략패턴의 정의입니다.
2.
하지만 작업반장이 없다고 전이를 할 수 없다고 생각하는건 큰 오산입니다. 위의 예에서 전이를 일으킬 수 있는 또 다른 사람이 있습니다. 누구입니까?
바로 접니다. 제가 100미터 들고가다가 힘들어서 ‘야 종희야, 와서 이것좀 들어라!’ 라고 할 수 있습니다. 이 상황에서 작업반장은 상당히 맘이 편합니다. 일단 첫번째 actor인 저에게 200미터 옮기라고만 하면 더 이상 신경쓰지 않아도 제가 종희에게, 종희가 어진이에게 전달해서 결국 200미터까지 옮겨지는 과정을 위임하기 때문입니다.
즉 전략패턴이 상태패턴이 되는 조건은 전이를 스스로 할 수 있기 때문입니다. 종희의 샘플에서는 호스트코드가 직접 aButton과 bButton에 actor인 AButtonState, BButtonState를 넣어주고 있습니다(일단 저 작명센스 자체가 완전히 상태패턴이 뭔지 모른다는 증거죠 ^^)
바른 예제는 aButton에게 최초 startState만 넣어주면 이후 startState가 여러 조건을 감안해서 스스로 alphaState로 전이한다던가 그 alphaState가 다시 overlayState로 전이된다던가 하는 식으로 구성했겠죠.
요점은 상태패턴의 actor들은 호스트의 통제를 받지 않고 스스로의 로직으로 actor들을 전이한다는 점입니다. 어떻게 스스로 actor를 전환할 수 있을까요? 크게 보면 두가지 방법이 있습니다.
1. 호스트가 actor에서 교체 메쏘드를 제공하는 경우
interface Ihost{
function transition($state:Istate):void;
}
class Host implements Ihost{
private var _state:Istate
public function Host():void{
_state=new ConcreateState1(this);//1. 구상상태에게 Ihost인 자신을 넘긴다.
}
//2. 구상 상태는 Ihost의 transition을 호출할 수 있으므로 스스로 전이를 일으킬 수 있다.
public function transition($state:Istate):void{
_state=$state;
}
}
class ConcreateState1 implements Istate{
private var _host:Ihost;
public function ConcreateState1($host:Ihost){
//생성자에서 Ihost를 받는다.
_host=$host;
}
...
private function transition($state:Istate):void{
//상태의 전이를 스스로 일으킬 수 있다!
_host.transition($state);
}
}
다른 방법은 래퍼상태객체를 사용하는 방법입니다. 래퍼를 제공하고 래퍼를 교체함으로서 호스트는 같은 상태인스턴스를 가리키고 있지만 내부에서 참조하는 상태객체는 달라지는 방법입니다.
class ConcreateState1 implements Istate{
//외부에 노출할 진짜 상태
private var _state:Istate;
public function ConcreateState1(){
//최초엔 자신을 제공한다.
_state=this;
}
//외부에 제공할 실제 상태객체
public function get State():Istate{return _state}
private function transition($state:Istate):void{
//상태의 전이를 스스로 일으킬 수 있다!
_state=$state;
}
}
상태패턴은 전이해야하는 로직은 호스트에게서 격리시키고 이후 상태전이를 변경할 수 있게(호스트가 관여할수없게)만들어줍니다.
이러한 시나리오는 게임에 흔합니다. 그전까지는 잠잔뒤 5분후에 깨나도록 설정되어있다가 기획상 3분후에 깨어나게 만들고 싶다면 기존엔 호스트 코드를 고쳐하는데 비해 상태패턴에서는 잠자기상태객체가 깨어나기상태객체를 호출하는 조건만 교체하면 됩니다.
따라서 상태패턴은 전문서적이 많습니다. 추천하고 싶은 도서는 Programming Game AI by Example 이란 책으로 사이텍미디어에서 역서가 나와있습니다. 실용적 예제로 본 게임인공지능 프로그램하기라고..