Archive for the ‘WPcomment’ Category.

쫑쫑쫑:[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 이란 책으로 사이텍미디어에서 역서가 나와있습니다. 실용적 예제로 본 게임인공지능 프로그램하기라고..

성진:Obsever Pattern

http://kindmonkey.tistory.com/entry/Obsever-Pattern

네이버 블로그에 있던 원본과 비교할 수 없을 만큼 발전되었습니다.

어지럽게 호스트코드에 만연했던 네이버 API관련 기능은 기능 클래스로 빠져나왔고, 옵저버프레임웍은 추상층만 제공하고 있습니다. 하지만 이제 시작일 뿐입니다.

첫번째로 네이버 API를 여러가지 종류로 사용하기 위해서 뺀 추상층이 INaverSubject 라면 이건 뭔가 이상하지 않을까요?(InaverAPI 라면 몰라도)  게다가 내부에 정의된 init와 getData는 아주 일반적인 기능이라 구지 네이버 전용의 추상층일리도 없습니다. 그저 IwebAPI정도가 적당할지도 모르겠군요. 하지만 그러기엔 애매한 점도 많고 아직까지 네이버 API군에 대한 추상층을 정의할 정보나 경험이 없는 상태라고 봐야하겠습니다.
요점은 추상층을 정의할 수 없기 때문에 하면 안된다는 겁니다.  따라서 NaverPopSearchWord 라는 구상클래스 하나를 정의하는 것으로 이 동네를 정리해야겠습니다. 이후 다른 NaverAPI를 클래스로 래핑해보고 공통점을 발견하게 된다면 그 때가서 추상층을 정의해보세요.

두번째는 구상Subject로 눈을 돌려봅시다.
SearchData란느 구상 subject는 사실 그 이름이 정확하지 않은게 화근이었습니다. 이 클래스의 진짜 이름은 다음과 같습니다.

SearchData_With_NaverPopSearchWord_on_TimerInterval
네이버실시간인기검색어를 타이머 인터벌로 제공해주는 서브젝트

무슨 말이냐 하면 구상 subject가 무엇인지 심각하게 생각해보지 않았다는 뜻입니다. 분명 저 Subject의 임무는 현재의 SearchData정도의 포괄적인 임무가 아니라 저런 임무였을 것 입니다. 이름으로 장난치는게 무슨 의미가 있냐구요?

있죠. 심각하게 있죠. 이름을 저렇게 생각하지 않았기 때문에 Main이 NaverSearch와 Timer를 갖고 있거든요. 그건요 사실은 전부 저 구상 subject가 가져야하는 거 랍니다.

마지막으로 Main은 어떻게 변할까요? 아마도 아래와 같아지길 기대합니다.

package
{
	import 옵저프레임
	import flash.display.Sprite;

	public class Main extends Sprite
	{
		public function Main()
		{
			var subject:ISubject;
			subject = new SearchData();
			subject.addObserver(new FavoriteSearchDisplay());
			subject.start();
			addChild(subject);
		}
	}
}

전에 네이버 블로그에도 같은 얘길 했지만 호스트가 뭘 그렇게 많이 알고 있나요 ^^;

달파란:검색 알고리즘

http://dalparan.kr/entry/검색-알고리즘

* 부분집합과 중복값
부분집합을 얘기할때는 원래 중복값에 대한 정의를 명확히 조건으로 명시해야합니다. 예를 들어 [3,3,4,4,5] 와 [3,4,5,6] 이 있을 때 앞에 배열에 뒤의 배열의 부분집합인지는 중복값 처리에 달렸습니다. 중복값을 허용하지 않는다면 모든 값을 고유하기 때문에 뒤의 배열에도 반드시 3, 4가 두 개 있어야만 부분집합이 될 수 있습니다.

달파란님의 소스는 일단 중복값이 없다를 가정한 데이터 군으로 되어있습니다. 이 경우엔 배제라는 기법을 쓸 수 있습니다.

var aAry: Array = [ 12, 2, 8, 6, 1 ];
var bAry: Array = [ 8, 9, 10, 15, 0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14 ];

function subset( a: Array, b: Array ): Boolean{
	var bClone:Array=b.concat();
	var cnt:int=0;
	var loopCount:int=0;//루프체크기
	for each(var i:* in a){
		for(var j:* in bClone){
			++loopCount;
			if(i==bClone[j]){
				++cnt;
				delete bClone[j];
				break;
			}
		}
	}
	trace(loopCount+":"+cnt);
	return a.length==cnt;
}
trace( subset( aAry, bAry ) );
//output
//36:5
//true

비정렬 상태의 배열을 돌려보면 배제하기 때문에 줄어듭니다. 같은 배열에 대해 달파란님이 처음 올린 순차 비교 알고리즘에서는 39회를 반복하고, 위의 배제알고리즘은 36회를 반복하게 됩니다(뭐 당연하다면 당연하겠죠 맞춘건 지워가니까 ^^) 당연히 포함관계가 많을수록 더 많이 지워가기 때문에 더욱 줄어듭니다. 위의 예에서 첫번째 배열의 요소가 더 많았다면 더욱 줄어드는 폭이 큽니다.

달파란:Command Patterns – 1

http://dalparan.kr/entry/Command-Patterns-1

오늘 마구 달리셨군요 ^^; 공부하시는 달파란님 화이팅입니다.
하지만 그래도 잘못된 건 알려드려야할 듯해서 몇 자 적겠습니다 ^^;

일단 Trace로 구현하신 것에 대해선 딱히 위축되실 필요는 없습니다. 학습과정이니 오히려 편안하게 만들어보는게 중요하죠. 하지만 예제가 좀 더 좋은 방향이었으면 하는 부분은 trace보다 invoker가 커맨드를 여러개 슬롯으로 관리했으면 좋았을텐데 하나만 들고 있는게 아쉽네요. 이 부분은 교재에서도 인보커가 슬롯으로 여러개의 커맨드를 소유하게끔 되어있었는데, 반영되었다면 더욱 좋았을걸하는 생각입니다.

일단 현재 샘플 상태의 인보커는 아래와 같은 문제점을 갖고 있습니다.

1. 인보커가 하나의 커맨드만 저장하기 때문에 커맨드 수만큼 인보커를 new해야합니다.
2. 현재 인보커는 커맨드를 호출한 것에 대해 어떠한 기록이나 캐쉬를 잡지 않습니다.
3. 호스트로 하여금 추상,구상 커맨드, 구상 리시버를 은닉하는 팩토리 기능이 없습니다.

아마 이 모든 문제가 그대로 UML에서 보이는 듯합니다.
일단 UML을 보면 호스트가 구상 커맨드는 물론 구상 리시버마저 알아야하는 상황입니다.
개선방향 중에 가장 바람직한 쪽은 호스트가 인보커만 아는 것입니다. 만약 그게 힘든 경우라면 추상커맨드를 아는 수준에서 멈춰야합니다.호스트가 그 이하의 구상층을 알아야한다면 이미 커맨드패턴이 아니라 퍼사드객체 내부용 다이어그램으로 변신됩니다(아니 오히려 퍼사드가 개입해야하는 상황으로 ^^)

현재도 호스트코드를 보고 있으면 warrior, warriorBattleCommand, Icommand, Controller, WarriorDefanceCommand 등 모든 클래스가 아주 강력하게 호스트와 바인딩 되어있습니다. 따라서 아래와 같은 호스트 코드가 되도록 수정해보세요 ^^;

package{
	import flash.display.Sprite;
	public class Main extends Sprite{
		public function Main(){
			var controller: Controller = new WarriorController();
			controller.add(Controller.attack,0);
			controller.add(Controller.defance,1);
			controller.add(Controller.training1,2);
			controller.add(Controller.training2,3);
			controller.add(Controller.training3,4);
			//칼로 공격합니다.
			controller.pressButton(0);
			//방패을 올립니다.
			controller.pressButton(1);
			//칼로 공격한 후 방패을 올립니다.
			controller.pressButton(2);
			//방패를 올린 후 칼로 공격합니다.
			controller.pressButton(3);
			//칼로 2번 공격합니다.
			controller.pressButton(3);
			//직전행동을 반복합니다.(칼로 2번공격)
			controller.redo();
			//히스토리상 현재로부터 과거로 카운트만큼 점프한 행동을 합니다.
			//현재 상태가 칼로2번공격이므로 2번이전은 칼로공격한후 방패를 올리는 상황임
			controller.history(2);
		}
	}
}

커맨드 객체가 하는 일 중엔 호스트에서 리시버를 격리 시키는 것도 큰 역활이랍니다 ^^;
또한 이는 어뎁터도 마찬가지입니다. 커맨드패턴의 구상커맨드 부분만 떼놓고 보면 어뎁터니까요.

림:변수선언에 대한 오해와 이해.. 그리고 인터페이스

http://limworld.tistory.com/entry/변수선언에-대한-오해와-이해-그리고-인터페이스

표현이 잘못된게 너무 많은데 그걸 다시 글에서 사람들에게 오해를 불러일으키게 사용하고 있네 ^^;

첫번째로는 타입, 형 이란 의미가 인터페이스로 해석되면 안되지.
형에 대한 이해가 부족한거 같아. 형이란 해당 변수가 차지할 메모리의 형태를 나타낸다. 최초엔 크기만 가졌지만 포인터를 갖게 되면서 다양한 인스턴스를 가리킬 수 있게 되었지. 따라서 인터페이스로 설명할게 아니라 형으로 설명해야 맞겠지. 여러가지 설명은 인터페이스가 맞지 않아서…라는 식이 아니라 형이 맞지 않아서로 고쳐야 오해가 없어.

두번째로 interface를 구상하는 클래스의 구상이유가 형지정에 응답하기 위해서라는건 어불성설이겠지.
인터페이스의 메쏘드를 구상해야하는건 컴파일러 문법상의 제약사항이고 반대로 인터페이스 자체를 형으로 지정하는 이유가 앞서서 설명하던 특정 형으로 캐스팅하여 정해진 기능만 사용하기 위해서라 할 수 있다.