flash/DesignPattern 에 해당하는 글3 개
2007/06/28   Buttons using a Design Pattern
2007/04/09   Template method pattern in AS2.0
2006/05/02   MVC vs. MVP

flash/DesignPattern | 2007/06/28 13:09

flash에서 구현되는 대부분의 사용자와의 Interaction 은 버튼을 통해 이루어 진다.
즉, 버튼을 통해 이벤트가 발생되고 그것을 통해 필요한 반응들을 처리하게 된다.
일반적으로 flash 에 버튼을 설정할때는 아래와 같이 직접 이벤트 핸들러 함수를 등록하여 사용한다.

button.onRelease=function(){  //code....}

하지만 이런 방법은 무비클립이나 버튼 자체에 코드가 종속되어 버리게 되어, 제어해야할 버튼이 많을 경우 이곳저곳에 이벤트 핸들러 함수를 작성해야하므로 체계적인 이벤트 플로우를 관리하기 위해서 좀 부족한 감이 든다.
또한 버튼은 단독으로 한개가 있는것보다는 여러개의 버튼이 컨텐츠의 네비게이션으로서 작용하는 경우가 대부분이다. 따라서, 버튼을 릴리즈 했을 경우 활성/비활성되는 부분도 고려해야하는 문제가 있다.

여기에서 소개하는 방법은 Design pattern 중에 Template method 패턴을 활용하여 버튼 로직을 구현하는 부분을 구현하고 실제적인 기능을 담당하는 부분은 추상클래스의 상속을 통해 구현하는 방법을 사용하였다.

기본적인 개념은 추상클래스 타입을 확장하여 각각의 구상클래스로서 버튼클래스를 제작한다.
그리고 추상클래스 타입을 이용해 작성된  ButtonManager class 에 버튼 클래스를 등록한다.
여기서 ButtonManager class 는 버튼 등록, 활성/비활성 체크 같은 전체적인 버튼을 관리하는 역할을 담당하게 된다.
(* 이곳에 사용된 startEvent 메소드는 기본적으로 제공하는 Dispatcher 클래스의 dispatchEvent 메소드를 재정의 한것이다. )

<AbstractSimpleButton Class>
버튼 추상 클래스로써 생성하는 모든 버튼에 공통적으로 상속되는 클래스이다.
이곳에 선언된 추상메소드를 구상클래스에서 오버라이드(override)하여 실제적인 기능을 구현한다.

import com.dstrict.UB.events.Dispatcher;
import com.dstrict.UB.events.Delegate;

class com.dstrict.UB.ui.button.AbstractSimpleButton extends Dispatcher {
 
 private var _mc:MovieClip;

 public function setController(inMc : MovieClip) : Void {
  _mc=inMc;
 }
 public function getController() : MovieClip {
  return _mc;
 } 
  /**---------------------------------------------------------------------------
     * Constructor function
     * @param inMc controller movieClip
   *---------------------------------------------------------------------------*/

 public function AbstractSimpleButton(inMc:MovieClip) {
  setController(inMc);
  setButton();
 }
 public function setButton():Void{
  getController().onRollOver=Delegate.create(this,onRollOver);
  getController().onRollOut=Delegate.create(this,onRollOut);
  getController().onPress=Delegate.create(this,onPress);
  getController().onRelease=Delegate.create(this,onRelease);
  getController().onReleaseOutside=Delegate.create(this,onReleaseOutside);
 }
 public function onRollOver() : Void {
  // override in sub-class
 }
 public function onRollOut() : Void {
  // override in sub-class
 }
 public function onPress() : Void {
 // override in sub-class
 }
 public function onRelease() : Void {
  // override in sub-class
 }
 public function onReleaseOutside() : Void {
  // override in sub-class
 }
 public function enabled(mode:Boolean):Void {
  // override in sub-class
 }
}

<ButtonManager Class>
추상클래스인 AbstractSimpleButton 클래스 타입을 이용하여 버튼의 등록및 이벤트 위임을 처리하고 있다.
어떠한 구상클래스(concrete class)도 사용되지 않았기 때문에 실제 구현과 완전히 분리되어 코드를 처리할 수 있다.

import com.dstrict.UB.ui.button.AbstractSimpleButton;
import com.dstrict.UB.events.ButtonEvent;
import com.dstrict.UB.events.Dispatcher;
class com.dstrict.UB.ui.button.ButtonManager extends Dispatcher{
 
 private var _buttonSet:Array;
 private var _selectButton:AbstractSimpleButton;
  
  /**---------------------------------------------------------------------------
     * Constructor function
   *----------------------------------------------------------------------------*/

 public function ButtonManager() {
  initialize();
 }
 
 //==================================================================
 ///////////public method //////////////////////////////////////////////
 //==================================================================

 public function addItem(item:AbstractSimpleButton):Void {
  _buttonSet.push(item);
  registerListener(item);
  killListener(item);
 }
 public function getItemAt(index:Number):AbstractSimpleButton{
  return AbstractSimpleButton(_buttonSet[index]);
 }
 public function kill():Void {
  for(var i in _buttonSet){
   AbstractSimpleButton(_buttonSet[i]).enabled(false);  
  }
 }
 public function live():Void {
  for(var i in _buttonSet){
   AbstractSimpleButton(_buttonSet[i]).enabled(true);
  }
 }
 
 //==================================================================
 ///////////event handler //////////////////////////////////////////////
 //==================================================================

 
 public function onPress(evt:ButtonEvent):Void {
  startEvent(new ButtonEvent(ButtonEvent.ON_PRESS,evt.target,evt.currentMovieClip));
 }
  public function onRelease(evt:ButtonEvent):Void {
  enabled(getSelectButton(),true);
  enabled(AbstractSimpleButton(evt.target),false);
  setSelectButton(AbstractSimpleButton(evt.target));
  startEvent(new ButtonEvent(ButtonEvent.ON_RELEASE,evt.target,evt.currentMovieClip));
 }
 public function onRollOver(evt:ButtonEvent):Void {
  startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OVER,evt.target,evt.currentMovieClip));
 }
 public function onRollOut(evt:ButtonEvent):Void {
  startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OVER,evt.target,evt.currentMovieClip));
 }

 //==================================================================
 ///////////private method //////////////////////////////////////////////
 //==================================================================

 private function initialize():Void {
  _buttonSet=new Array();
 }
 private function setSelectButton(button:AbstractSimpleButton):Void {
  _selectButton=button;
 }
 private function getSelectButton():AbstractSimpleButton{
  return _selectButton;
 }
 private function registerListener(item:AbstractSimpleButton):Void {
  item.addEventListener(ButtonEvent.ON_ROLL_OVER,this);
  item.addEventListener(ButtonEvent.ON_ROLL_OUT,this);
  item.addEventListener(ButtonEvent.ON_PRESS,this);
  item.addEventListener(ButtonEvent.ON_RELEASE,this);
 }
 private function killListener(item:AbstractSimpleButton):Void {
  var owner:ButtonManager=this;
  item.getController().onUnload=function(){
   item.removeEventListener(ButtonEvent.ON_ROLL_OVER,this);
   item.removeEventListener(ButtonEvent.ON_ROLL_OUT,this);
   item.removeEventListener(ButtonEvent.ON_PRESS,this);
   item.removeEventListener(ButtonEvent.ON_RELEASE,this);
  };
 }
 private function enabled(button:AbstractSimpleButton,mode:Boolean):Void {
  button.enabled(mode);
 }

}

event handler 함수를 보면 등록되어 있는 추상클래스 버튼에서 발생한 이벤트를 받아서 다시 최종적으로 ButtonManager 에 등록되어있는 이벤트 리스너 함수로 넘겨준다. 이 과정에서 필요한 버튼 이벤트 오브젝만 넘겨준다.


<ButtonEvent Class>
버튼에 대한 이벤트 정보를 담고 있는 이벤트 오브젝트.

class com.dstrict.UB.events.ButtonEvent extends com.dstrict.UB.events.Event {

 public static var ON_ROLL_OVER:String = "onRollOver";
 public static var ON_ROLL_OUT:String = "onRollOut";
 public static var ON_PRESS:String = "onPress";
 public static var ON_RELEASE:String = "onRelease";
 public static var ON_RELEASE_OUTSIDE:String = "onReleaseOutside";

 public var currentMovieClip:MovieClip;
 
 /**--------------------------------------------------------------------------
 *constructor function
 *@param Type : String, 이벤트 핸들러 메서드
 *---------------------------------------------------------------------------*/

 public function ButtonEvent(Type : String,Source : Object,curMovieClip:MovieClip) {
  super(Type,Source);
  currentMovieClip=curMovieClip;
 }
 }



<SampleConcreteButton  Class>
실제 버튼 기능을 구현하기 위한 구상 클래스. 단순히 버튼 롤오버 반응과 릴리즈시 활성체크를 구현하였다.
생성하려는 버튼의 기능에 따라 이 클래스는 다르게 구성된다. 물론 추상메소드로서 작용하는
AbstractSimpleButton 클래스의 onRollOver,onRollOut,onRelease,onReleaseOutside, enabled
는 오버라이드(override) 하여 구현하여야 한다.

import com.dstrict.UB.ui.button.AbstractSimpleButton;
import com.dstrict.UB.events.ButtonEvent;
class com.dstrict.UB.ui.button.SampleConcreteButton extends AbstractSimpleButton {
 
 public var buttonIndex:Number;
 
 public function SampleConcreteButton(inMc : MovieClip,inButtonIndex) {
  super(inMc);
  buttonIndex=inButtonIndex;
 }
 
 public function onRollOver() : Void {
  getController().gotoAndStop(2);
  startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OVER,this,getController()));
 }

 public function onRollOut() : Void {
  getController().gotoAndStop(1);
  startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OUT,this,getController()));
 }
   
 public function onRelease() : Void {
  startEvent(new ButtonEvent(ButtonEvent.ON_RELEASE,this,getController()));
 }

 public function onReleaseOutside() : Void {
  startEvent(new ButtonEvent(ButtonEvent.ON_RELEASE,this,getController()));
 }
 
 public function enabled(mode:Boolean):Void {
  getController().enabled=mode;
  if(mode){
   getController().gotoAndStop(1);
  }else{
   getController().gotoAndStop(2);
  }
 }

}


<sample code>
위에서 제작한 클래스를 활용하여 버튼 클래스를 생성하고 등록하는 코드
간단한 4개의 버튼을 구현하고 있다. 사용자는 SampleConcreteButton 를 만들어 버튼을 등록하기만 하면 모든 이벤트를 한꺼번에 관리할 수 있다.

import com.dstrict.UB.ui.button.ButtonManager;
import com.dstrict.UB.ui.button.AbstractSimpleButton;
import com.dstrict.UB.ui.button.SampleConcreteButton;
import com.dstrict.UB.events.ButtonEvent;

var buttonMgr:ButtonManager=new ButtonManager();
buttonMgr.addEventListener(ButtonEvent.ON_RELEASE,this);

buttonMgr.addItem(new SampleConcreteButton(number1_mc,1));
buttonMgr.addItem(new SampleConcreteButton(number2_mc,2));
buttonMgr.addItem(new SampleConcreteButton(number3_mc,3));
buttonMgr.addItem(new SampleConcreteButton(number4_mc,4));

//event handler
function onRelease(evt:ButtonEvent):Void{
 result_txt.text="onRelease event : "+evt.currentMovieClip+"----->"+evt.target.buttonIndex;
}

//Release event
var numberButton:AbstractSimpleButton=buttonMgr.getItemAt(0);
numberButton.onRelease(new ButtonEvent(ButtonEvent.ON_RELEASE,numberButton));


위 코드에는 ButtonManager에 등록되어있는 리스너가 onRelease 이벤트 밖에 없기 때문에
릴리즈 이벤트만 최종적으로 처리한다. 다른 버튼 이벤트를 처리하고 싶으면 리스너에 이벤트를
추가 하여 이벤트 핸들러 함수만 구현하면 된다.

등록된 모든 이벤트는  ButtonManager 의 이벤트 핸들러 함수를 통해 전달된다. 따라서 버튼에 대한 이벤트 플로우를 한곳에 집중할 수 있으며 기존에 코드에 종속되어있는 버튼에 비해 유연성이 크게 향상된다.

sample code 에서 맨 마직막 부분의 release event 부분은 사용자가 마우스를 통해 버튼을
클릭해서 release event 를 발생시키는 것이 아니라 직접 임의로 발생시킬 수 있다.

download sample........


 
 
태그 : button interaction, design pattern, flash, template method
이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/98

Name 
Password 
Homepage 
  secret
Comment 
  글쓰기

flash/DesignPattern | 2007/04/09 21:22

Template method 패턴은 디자인 패턴 중에서도 가장 기본이 되는 패턴중에 하나다.
템플릿이란 단어에서 유추할 수 있듯이 어떤 일정한 틀을 기본으로 클래스를 구성하는 방식이다.
추상클래스(Abstract class) 에 정의되어있는 Abstract method 를 기반으로 알고리즘을 구성하는 template method 를 만들어 동작시키는 것이다. 다시말해 알고리즘의 캡슐화(encapsulation) 라고 할수 있다.

추상클래스(Abstract class) 에서는 단지 알고리즘만 기술하는 역할만 한다. 실제 구현은 서브클래스 (Concrete class) 에서 기술한다. 따라서 Template method 를 이용하면 알고리즘의 구조는 유지하면서 서브클래스에서 추상메소드를 오버라이드하여 재정의 할 수 있다.
서브클래스에서 행동을 지정할 수 있게 하면서도 코드의 재사용이 가능한 특징으로  Template method 패턴은 framework 를 제작하는데 자주 등장한다.

플래시에서는 abstract 키워드가 지원되지 않지만 개념상 큰차이없이 패턴을 사용할 수 있다.

<Abstract class>

class AbstractButton {
 
 private var _mc:MovieClip;
 
 public function setController(inMc : MovieClip) : Void {
  _mc=inMc;
 }
 public function getController() : MovieClip {
  return _mc;
 }
 
 
 //Constructor function
 function AbstractButton(inMc:MovieClip){
  setController(inMc);
  setButton();
 }
 
 
 //Template method
 public function setButton():Void{
     getController().onRollOver=Delegate.create(this,onRollOver);
     getController().onRollOut=Delegate.create(this,onRollOut);
     getController().onRelease=Delegate.create(this,onRelease);
     getController().onReleaseOutside=Delegate.create(this,onReleaseOutside);
 
 }
 
 public function onRollOver() : Void{
  // override in sub-class
  throw new Error("Abstract method!!!!!");
 }

  public function onRollOut() : Void{
   // override in sub-class
  throw new Error("Abstract method!!!!!");
 }

  public function onRelease() : Void{
  // override in sub-class
  throw new Error("Abstract method!!!!!");
 }

  public function onReleaseOutside() : Void{
   // override in sub-class
  throw new Error("Abstract method!!!!!");
 }
 
 
  public function enabled(mode:Boolean):Void{
  // override in sub-class
  throw new Error("Abstract method!!!!!");
 }
 
}


<Concrete class>

class ConcreteButton extends AbstractButton{
 
 function ConcreteButton(inMc:MovieClip){
  super(inMc);
 }
 
 
public function onRollOver() : Void{
  getController().gotoAndPlay("over");
 }

 public function onRollOut() : Void{
  getController().gotoAndPlay("out");
 }

 public function onRelease() : Void{
  getController().gotoAndPlay("release");
 }

 public function onReleaseOutside() : Void{
 
 }
 
 public function enabled(mode:Boolean):Void{
  getController().enabled=mode;
 }
 
}

위 sample 은 버튼을 구성하는 간단한 클래스이다. 
플래시에서 Interaction 을 구현할때 거의 대부분버튼을 구현하여 접근한다. 일반적으로 버튼 액션을 구현하려면 버튼 인스턴스나 무비클립 인스턴스를 통해 버튼 이벤트를 주어서 사용하지만 그럴경우 제어해야할 버튼이 많아지면 코드길이가 늘어날 뿐더러 코드 유연성이 떨어지게 된다.

실제 버튼을 구현하려면 추상클래스를 확장하여 추상메소드를 구현하기만 하면 된다.
추상클래스의 추상메소드의 정의는 개발자가 신중히 결정해서 정해야한다. 추상메소드가 너무 많게 되면 서브클래스에서 구현해야할 코드가 많아져 불편한점이 있을 수 있다. 반대로 너무 추상메소드 자체를 큰 덩어리로 나누게되면 유연성을 떨어뜨리게 된다.


 
 
태그 : As2.0, design pattern, flash, template method
이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/92
Tracked from { ActionScript 3.0 } 2007/05/16 23:04
제목 : [디자인패턴] 2. 템플릿메소드패턴
사용이 예측되는 메소드들의 집합을 수행하는 템플릿 메소드를 만들고(initialize()), 서브클래스단에서 메소드들을 정의해준다.initialize 메서드의 알고리즘은 그대로 유지한체 추상메서드들만 서브클래스에서구현을 함으로서 코드의 재사용성을 높인다.ex) 인터렉션 버튼(롤오버롤아웃등의 이벤트리스너 등록을 템플릿화하고 리스너함수는 서브클래스에서 제작) package game{ /* 게임의 템플릿에 해당하는 추상클래스 abstract 클래스가 없는..

Name 
Password 
Homepage 
  secret
Comment 
  글쓰기

flash/DesignPattern | 2006/05/02 10:37
http://www.darronschall.com/weblog/archives/000113.cfm
 
 
태그 : design pattern, flash
이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/9

Name 
Password 
Homepage 
  secret
Comment 
  글쓰기


[PREV] [1] [NEXT]

 
전체 (105)
flash (74)
math&physics (4)
programming (11)
Flex2 (1)
Mac (2)
photo (0)
project (6)
주저리주저리 (3)
유용한 자료들 (1)
diary (0)
Book (1)
web (2)
«   2009/01   »
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31