PureMVC #3 (Hello Flash 2)

지난시간에 이어 이번 글은 아래 pureMVC 측에서 AS3.0 예제로 제공하는 소스를 가지고 설명하겠습니다.
http://puremvc.org/pages/downloads/AS3/Demo_AS3_Flash_HelloFlash.zip

이번엔 Mediator 와 Proxy 에 관해 알아보겠습니다.
용어 해석으로는
Mediator 는 중재인, 조정자, 매개자 라고 하네요.
Proxy 는 대리권, 대리, 위임장 등으로 나오는 군요^^

Mediator 는 View 에 있는 UI 등의 Interaction 반응을 처리해주는 역활을 주로 하고 화면에 보여주는 모든 부분에 대한  처리를  한다고보시면 됩니다.

Proxy 는 Model 의 서브 클래스로 프로그램에서 중점적으로 처리해줘야 할 알고리즘이나 로직에 관한 풀이를
하고 있습니다. 또한 Proxy 는 로직 내부에서 생성되는 이벤트를 통지할 수는 있지만 따로 생성된 이벤트를 받을 수는 없습니다. 따라서 자연스럽게 이벤트를 받아 처리해주는 부분은 Command 와 Mediator 이렇게 두 부분을 나뉘게 됩니다.

이번 시간 처음 볼 소스는
StageMediator.as 가 되겠습니다.


package org.puremvc.as3.demos.flash.helloflash.view
{
        import flash.events.Event;
        import flash.display.Stage;
        import flash.geom.Rectangle;
        import flash.events.MouseEvent;

        import org.puremvc.as3.interfaces.*;
        import org.puremvc.as3.patterns.mediator.Mediator;

        import org.puremvc.as3.demos.flash.helloflash.ApplicationFacade;
        import org.puremvc.as3.demos.flash.helloflash.view.HelloSpriteMediator;
        import org.puremvc.as3.demos.flash.helloflash.view.components.HelloSprite;
        import org.puremvc.as3.demos.flash.helloflash.model.SpriteDataProxy;

        /**
        * A Mediator for interacting with the Stage.
        */

        public class StageMediator extends Mediator implements IMediator
        {
                // Cannonical name of the Mediator
                public static const NAME:String = ‘StageMediator‘;


다른 모든 MVC 의 서브 클래스들 과 마찬가지로 각각의 패턴과 인터페이스를 상속받아 생성되며
각각 자신만의 NAME 과 같은 문자열 구분자로 서로를 구분하고 있습니다.

                /**
                * Constructor.
                */

                public function StageMediator( viewComponent:Object )
                {
                        // pass the viewComponent to the superclass where
                        // it will be stored in the inherited viewComponent property
                        super( NAME, viewComponent );

역시 마찬가지로 상속받은 상위클래스를 초기화 시켜주기 위해 super 메소드를 호출해 줍니다.
이때 Mediator 의 경우 View(화면 객체) 에 해당하는 객체를 같이 넘겨주어 이 클래스 전체에서 사용하게 됩니다.



                        // Retrieve reference to frequently consulted Proxies
                        spriteDataProxy = facade.retrieveProxy( SpriteDataProxy.NAME ) as SpriteDataProxy;

                        // Listen for events from the view component
                        stage.addEventListener( MouseEvent.MOUSE_UP, handleMouseUp );
                        stage.addEventListener( MouseEvent.MOUSE_WHEEL, handleMouseWheel );

                }


                /**
                * List all notifications this Mediator is interested in.
                * <P>
                * Automatically called by the framework when the mediator
                * is registered with the view.</P>
                *
                * @return Array the list of Nofitication names
                */

                override public function listNotificationInterests():Array
                {
                        return [ ApplicationFacade.STAGE_ADD_SPRITE
                        ];
                }

Mediator 에서는 현재 View 에서 반응해야할 이벤트(공지)들만 선별하여 이벤트를 받겠다고 등록해야 합니다. Flash Event 의 경우 addEventLitener 를 통해 함수와 바로 매칭시켜 주지만, Mediator의 경우 listNotificationInterests 라는 함수를 재정의(override) 하여 등록하게 됩니다.
등록 방법은 위와같이 공지받고 싶은 문자열을 배열로 return 해주면 완료됩니다.
이 함수는 현재 클래스의 Mediator 가 등록될 때 자동으로 호출되며 해당 공지들을 받겠다고 등록되어 집니다.
이곳에서는 STAGE_ADD_SPRITE 라는 공지를 받겠다고 등록해 놓았네요.
이렇게 등록되면 나중에 어느 위치에서도 해당 공지가 발생 할 때 현재 클래스로 공지가 들어오게 됩니다.
공지를 받는 부분은 아래 handleNotification 메소드 를 통해 받게 됩니다.

                /**
                * Handle all notifications this Mediator is interested in.
                * <P>
                * Called by the framework when a notification is sent that
                * this mediator expressed an interest in when registered
                * (see <code>listNotificationInterests</code>.</P>
                *
                * @param INotification a notification
                */

                override public function handleNotification( note:INotification ):void
                {
                        switch ( note.getName() ) {

                                // Create a new HelloSprite,
                                // Create and register its HelloSpriteMediator
                                // and finally add the HelloSprite to the stage
                                case ApplicationFacade.STAGE_ADD_SPRITE:
                                var params:Array = note.getBody() as Array;
                                var helloSprite:HelloSprite = new HelloSprite( spriteDataProxy.nextSpriteID, params );
                                facade.registerMediator(new HelloSpriteMediator( helloSprite ));
                                stage.addChild( helloSprite );
                                break;
                        }
                }

바로 위에서 등록된 이벤트(공지) 들이 발생하여 전달될 때 받아오는 부분 입니다.
함수 파라메터 로는 note:INotification 를 받아오게 됩니다.
사용자는 이 공지(note)의 이름을 검사하여 해당 공지가 어떤 공지인지를 알아내고 그에 맞추어 명령들을 실행해 줍니다.
이 예제에서는 ApplicationFacade.STAGE_ADD_SPRITE 하나만 등록되어있기 때문에 공지가 오면
무조건 ApplicationFacade.STAGE_ADD_SPRITE 밖에 없겠군요 ㅎㅎ..
지난시간 설명드린것 과 같이 공지를 사용하는 몇몇 함수들 [ note.getName(), note.getBody() ] 을 통해
작업을 진행하게 됩니다.
여기서는 HelloSprite 객체를 만들며 만들어진 객체에 Mediator를 등록 시키고 화면에 추가해 주는 역활을 하고 있군요.


                // The user has released the mouse over the stage
                private function handleMouseUp(event:MouseEvent):void
                {
                        sendNotification( ApplicationFacade.SPRITE_DROP );
                }

                // The user has released the mouse over the stage
                private function handleMouseWheel(event:MouseEvent):void
                {
                        sendNotification( ApplicationFacade.SPRITE_SCALE, event.delta );
                }

                /**
                * Cast the viewComponent to its actual type.
                *
                * <P>
                * This is a useful idiom for mediators. The
                * PureMVC Mediator class defines a viewComponent
                * property of type Object. </P>
                *
                * <P>
                * Here, we cast the generic viewComponent to
                * its actual type in a protected mode. This
                * retains encapsulation, while allowing the instance
                * (and subclassed instance) access to a
                * strongly typed reference with a meaningful
                * name.</P>
                *
                * @return stage the viewComponent cast to flash.display.Stage
                */

                protected function get stage():Stage{
                        return viewComponent as Stage;
                }

마지막으로 viewComponent 를 사용자가 정의한 view 의 형에  맞추어주는 케스팅 작업이 남았습니다.
꼭 필요한 부분은 아니지만 Mediator 의 경우 이렇게 정의하여 사용하면 보다 직관적이고 편리하게 사용할 수 있어 적극 추천하는 부분 입니다.
주석만 보셔도 자세한 내용을 알 수 있겠지만 간단히 다시 설명 드리자면
현재 사용중인 Mediator 는 view 즉 화면에 보이는 어느 DisplayObject 에 대해 UI 및 Interaction 을 관리해주는 부분 입니다. 따라서 DisplayObject 에 속해있는 각 버튼들 부터 사용자 반응을 받아낼 모든 부분들에 접근하여 등록하거나 처리할때 아주 빈번히 view 객체를 참조하여야 합니다.
예를들어 화면의 버튼에 간단한 MouseEvent.CLICK 이벤트를 하나 걸고 해당 이벤트 작동 시 pureMVC 전체에 공지를 보내고 싶을 때 화면의 버튼에 쉽게 접근하여 이벤트를 등록시켜 줄 수 있어야 합니다.
따라서 Mediator 등록시 생성자로 들어온 viewComponent 를 실제 DiplayObject 형식에 맞추어 get 으로 등록해 주면 매번 viewComponent 를 캐스팅 해주지 않고도 원래의 속성처럼 쉽게 사용할 수 있게 해 줍니다.
여기에서는 Stage 로 캐스팅 해주고 있군요.


                private var spriteDataProxy:SpriteDataProxy;
        }
}









다음으로 마지막 남은 Model 의 서브 클래스 Proxy 가 되겠습니다.
간단한 설명은 위에서도 했고 바로 소스를 보시겠습니다.
이번에 보실 Proxy 소스는 위의 Mediator 에서 나온 SpriteDataProxy 입니다.
SpriteDataProxy.as


/*
PureMVC AS3 / Flash Demo – HelloFlash
By Cliff Hall <clifford.hall@puremvc.org>
Copyright(c) 2007-08, Some rights reserved.
*/

package org.puremvc.as3.demos.flash.helloflash.model
{
        import org.puremvc.as3.interfaces.IProxy;
        import org.puremvc.as3.patterns.proxy.Proxy;

        public class SpriteDataProxy extends Proxy implements IProxy
        {
                public static const NAME:String = ‘SpriteDataProxy‘;

                public function SpriteDataProxy( )
                {
                        super( NAME, Number(0) );
                        palette = [ blue, red, yellow, green, cyan ];
                }

Mediator 의 경우 super 의 두번째 인자가 viewComponent 였다면 Proxy 의 두번째 인자는 data 입니다.
이 예제의 Proxy 의 경우 data 를 Sprite의 생성 카운터로 사용하고 있기 때문에 그냥 Number(0) 과 같이 초기값을 등록해 주고 있습니다.


                private var palette:Array;
                private var red:uint = 0xFF0000;
                private var green:uint  = 0x00FF00;
                private var blue:uint   = 0x0000FF;
                private var yellow:uint = 0xFFFF00;
                private var cyan:uint = 0x00FFFF;

                //
                public function nextSpriteColor( startColor:uint ):uint
                {
                        // identify color index
                        var index:int;
                        var found:Boolean = false;
                        for ( var j:int=0; j<palette.length; j++)
                        {
                                index = j;
                                if (startColor == palette[index]) break;
                        }

                        // select the next color in the palette
                        index = (index == palette.length-1)?0:index+1;
                        //return startColor;
                        return palette[index];

                }

                /**
                * Get the next Sprite ID
                */

                public function get nextSpriteID():String{
                        returnsprite“+spriteCount++;
                }

                /**
                * Get the next Sprite ID
                */

                public function get spriteCount():Number{
                        return data as Number;
                }

                public function set spriteCount(count:Number):void
                {
                        data = count;
                }

Model 의 로직 부분이라 그런지 pureMVC 만의 특별한 기능이나 메소드 들은 없습니다.
단순히 data 를 spriteCount 로 get/set 등록해주어 사용하는것과 몇몇 로직 역활을 하는 사용자 정의 함수들로 구성되어 있습니다.


        }
}

Proxy 는 설명드린것과 같이 어떤 작은 데이터 모델을 나누어 캡슐화 해 놓은 것 입니다.
따라서 Proxy 는 각 데이터 모델에 따라 다양한 형태로 만들어지고 쓰일 수 있습니다.

가장 간단한 형태는 위의 예제에서와 같이 어떤 객체들의 색상및 순번등의 간단한 데이터를 관리하는 목적으로
쓰일 수 있으며 다른 용도로는 Remote Proxy 와 같이 통신 등 에서 서버와 주고받는 데이터를 관리하는 형태,
또는 접근을 돌려 제한적으로 사용하게 해주는 Protection Proxy, 어떤 객체를 로딩하거나 시간이 지연되는 형태의 처리 혹은 생성된 객체들의 관리 등을 해주는 Smart Proxy 등.. 여러 형태로 사용이 가능합니다.


아래는 이 예제의 실행 파일 입니다.


※ 시간이 지나면 사각형이 점점 작아지는 예제입니다.. 너무 작아져서 화면에 잘 안 보일 수도 있습니다.
휠을 돌리면 스케일 조절이 되고 사각형 드레그시 객체 생성 됩니다.








여기까지 간단한 예제 한개를 보면 각 부분에 대해 짧게나마 설명을 마쳤습니다.
pureMVC 프레임웍의 사용은 협업시나 대형 프로젝트의 진행 혹은 코드의 재활용 및 잦은 수정등에 적합한 듯 보입니다.
다만 꼭 장점만 있는것 같아 보이지는 않습니다.
이벤트(공지)의 경우 단순 문자열로 처리하기 때문에 혹여나 multiCore 버젼 사용의 경우 다른 비슷한 이벤트와의 충돌이 있을 수도 있으며 각종 Proxy 나 VO( value object ) 등의 경우 캡슐화에 생성 및 사용에 상당부분 코딩노가다(?) 작업이 필요합니다.
물론 잘 정의된 프로젝트가 나중에 진가를 발휘하듯 이런 노력이 절대 쓸모 없는 것은 아닙니다.
이런 프레임 웍을 다뤄보지 않으신 분들이라면 한번쯤은 꼭 써보신다면 분명히 도움이 되지 않을까 생각됩니다.



Fabrication

추가적으로 위에 언급한 사용상의 불편한 점 및 유용한 기능들을
http://www.codedrunks.com/
이곳에서 Fabrication 이라는 이름으로 pureMVC 유틸 비슷하게 개발중인것 같습니다.
현재 0.6 버젼이 Release 되어있으니 관심 있으신 분들은 들러보시기 바랍니다.
http://code.google.com/p/fabrication/wiki/Features
이곳은 Fabrication 을 다운받는 곳과 간략한 설명 그리고 예제가 있는 곳 입니다.


 






PureMVC #1

pureMVC 알아보기

이번에는 잠시 3D 부분을 떠나 패턴 관련 내용을 알아보도록 하겠습니다.

먼저 알아볼 대상인 pureMVC 에 대해 설명드리자면

pureMVC는 하나의 프레임 워크라 볼 수 있습니다.
프레임워크를 사전 그대로 풀이하자면 (뼈대. 골격. 틀.) 이라는 뜻이 있는데요.
프로그램 개발시에 필요한 기반을 제공한다고 생각해 주시면 됩니다.
이런 프레임 워크는 공개된 것들만 해도 상당수가 있는데요.
플래쉬 AS 쪽에선 캔곰, pureMVC 등이 많이 사용되고 있습니다.
이 중에서 pureMVC 는 특히 여러가지 언어로 ( c, c#, php, java, objective C… ) 공개되어있는데요..
가장 기본적인 MVC 패턴만을 제공해 주는 정말 심플한 프레임웍이라 생각됩니다. (켄곰 등에 비해)
따라서 플래쉬만의 특화된 기능이나 이런것 보다는 기본에 충실한 프레임웍인것 같습니다.

pureMVC 에 대한 서론은 이쯤에서 마치겠습니다.
 
정말 자세한 사항이나 궁굼한 점은.
저의 사견이 들어가지 않은 제작자의 홈페이지로 가셔서 확인해 주세요^^; ㅎㅎ

pureMVC : http://www.puremvc.org/


그럼 가장 먼저 설명 드릴 디자인 패턴 인 MVC 패턴에 대해 알아보겠습니다.


MVC

MVC
Model, View, Contoller 의 약자 입니다.

Model : 프로그램의 Data, 로직 등 프로그램 동작의 뼈대가 되는 부분 입니다.
View   : 사용자 화면에 보여지는 UI, 및 화면 구성요소들을 나타냅니다.
Contrioller : 위의 Model 과 View 사이에서 둘 사이를 이어주는 매개 역활을 합니다.


프로그램 제작에 있어 위와같이 각각의 역활을 정확히 분배하여 나누어주는 것이 이 패턴의 가장 중요한 점이자.

pureMVC 의 핵심(?) 이라 볼 수 있습니다.

이렇게 각각의 역활을 나누어주는가장 큰 이유로는 코드의 재사용성을 둘 수 있습니다.
극단의 예를 들어 로컬에서 돌아가는 게임을 웹에서 돌아가게 만들라고 해도.
화면에 보여주는 부분인 View 부분만을 웹에서 통용적으로 사용되는 것으로 바꾸어 준 후 네트웍 기능을
추가하면 model 입장에서는 크게 게임 로직이 바뀌는 일 없이 사용 할 수 있다는 것 입니다.

다른 이유로는 이렇게 MVC 패턴에 맞게 제작된 프로그램은 개발자도 모르는 사이에 느슨한 결합을 통한 코딩이
됩니다.
따라서 개발 중간 초기 기획과는 많이 틀어진 방향의 내용이 새로 추가되거나 변경되었을 경우에도.
어느정도 더 수월하게 작업을 진행할 수 있다는 점 입니다.

다음으로 facade 패턴에 대해 알아보겠습니다.


Facade

facade
(퍼사드) 는 복잡한 서브시스템에서 공통의 인터페이스들을 뽑아 사용자가 쉽게 사용할 수 있도록
해주는 패턴 입니다.

pureMVC 에서는 Model, View, Controller 들을 각각 통합해 관리해 주는 역활을 하고 있습니다.

마지막으로 Observers 패턴 입니다.


Observers

Observers. 이 패턴은 플래쉬로 비교하면 이미 모두 능숙하게 사용하시는 EventDispatch 와 흡사합니다.
마찬가지로 어떤 이벤트가 발생 했을때 그 이벤트를 필요로하는 곳에서 이벤트를 받겠다고 등록한 곳으로만
이벤트 발생 여부를 전달합니다.

플래쉬의 Event 와 조금 다른 점은 플래쉬와 같이 이벤트가 Bubble 형식으로 전달되지 않는다는 것 과.
pureMVC 에서는 해당 이벤트를 Notification 할 때 이벤트 속성과 함께 사용자 임의의 값을 함께 보내어
이벤트를 받는 곳에서 이를 사용할 수 있다는 것 입니다.



아래 그림을 보면 위에 설명한 각각의 패턴들의 연관성을 짐작(?) 하실 수 있습니다.




사실.. 그냥 보기에도 복잡합니다 ㅡㅡ;;
크게 볼 때 M,V,C 의 각 부분이 있고 이들을 Facade 패턴이 감싸고 있어서 어디서든 원하는 데이터와 작업에
대한 접근을 가능하게 해준다.. 정도로 이해하면 될 것 같습니다.



아. 미리 설명드리지 못한 내용이 하나 남았습니다.


pureMVC 에서는 MVC 패턴을 사용하면서 해당 역활을 수행하는 서브 클래스들을 다른 명칭으로 부릅니다.


명칭은 아래와 같습니다.



              MVC             =         서브 클래스














 Model


 Proxy


 View


 Mediator


 Controller


 Command

다음 글 부터는 pureMVC 측에서 제공하는 AS3.0 용 pureMVC 예제를 사용하여 설명 하도록 하겠습니다.