ALL ABOUT ME

Flash :: [AS2.0/AS3.0] 싱글톤 디자인패턴의 구현과 종류 본문

Lab

Flash :: [AS2.0/AS3.0] 싱글톤 디자인패턴의 구현과 종류

threeword 2010. 3. 30. 15:40

디자인 패턴 중 가장 이해가 빠르고 쉽게 접근할 수 있는 패턴이...

바로 싱글톤 패턴이 아닌가 싶습니다.

이 싱글톤 디자인 패턴이란..

쉽게 말해서, 단 하나의 클래스 인스턴스를 생성한다는 것입니다.

공통으로 사용하는 클래스(util class)에 적용하면 알맞을 것입니다.

예를들어 Trace를 찍어주는 TraceManager가 있다고 하면,

각각의 다른 클래스에서 Trace를 찍어 줄때마다

매번 TraceManager 클래스 인스턴스를 생성할 수는 없는 일입니다.

이것은 컴퓨터 자원을 낭비하는 일이고,

여러 인스턴스들이 생기다보면 서로 방해작용을 할 수도 있을 것입니다.

하지만,

싱글톤 패턴은 하나의 인스턴스에 접근 할 수 있는 전역적인 방법이므로,

남발해서는 안될 것입니다.

액션 스크립트 버전에 따라 그 구현방법도 다르므로,

이번에는 AS 2.0과 AS 3.0에서 각각 구현방법을 알아보도록 하겠습니다.


[AS 2.0]

class Singleton {
     private static var _instance:Singleton = null;

     private function Singleton(){
     }

     public static function getInstance():Singleton{
          if(Singleton._instance == null){
               Singleton._instance = new Singleton();
          }
          return Singleton._instance;
     }
    
     public function doSomething():Void{
          trace("do something");
     }
}


AS 2.0과 AS 3.0의 차이는,

생성자가 private로 선언되었냐 아니면 public으로 선언되었냐 의 차이입니다.

AS2.0 에서는 생성자를 private 로 선언 할 수가 있었기 때문에

자바와 같이 싱글톤 패턴을 쉽게 구현 할 수 있었습니다.


[AS 3.0]

반면, AS 3.0에서는 생성자를 private로 선언하는 것을 지원하지 않으므로...
많은 대체 방법들이 있습니다.
그 중 네가지 편법(?)을 소개해 드릴까 합니다.


i) 내부 클래스를 이용하는 방식
package{
     public class Singleton{
    
     private static var _instance:Singleton;

     public function Singleton(singletonEnforcer:SingletonEnforcer){
          if (!(singletonEnforcer is SingletonEnforcer)){
               throw new Error("인스턴스를 생성할 수 없습니다.");
          }
     }

     public static function getInstance():Singleton{
          if (Singleton._instance == null){
               Singleton._instance = new Singleton(new SingletonEnforcer());
          }
          return Singleton._instance;
     }

     public function doSomething():void{
          trace("do something");
     }
}

class SingletonEnforcer {}

이 방법은 편법 중에서도 고전적이고 정석이라고 할 수 있는 방법으로,

" SingletonEnforcer " 라는 내부클래스를 사용하여 싱글톤을 구현 한 방법입니다.
SingletonEnforcer 라는 내부클래스는 같은.as 파일 내에서만 접근이 가능하기 때문에
외부에서 생성자의 접근을 막을 수 있습니다. (생성자가 public임에도 불구하고...)

따라서, Singleton.getInstance().doSomething() 으로 클래스에 접근하여
단 하나의 클래스 인스턴스를 생성하여 일을 처리할 수가 있습니다.


ii) 제어변수를 이용한 방식
package{
     public class Singleton{
    
     private static var _instance:Singleton;
     private static var _creatingSingleton:Boolean = false;

     public function Singleton(){
          if (!_creatingSingleton){
               throw new Error("인스턴스를 생성할 수 없습니다.");
          }
     }

     public static function getInstance():Singleton{
          if (!_instance){
                 _creatingSingleton = true;
                 _instance = new Singleton();
                 _creatingSingleton = false;
          }
          return _instance;
     }

     public function doSomething():void{
          trace("do something");
     }
}


_creatingSingleton
이라는  Boolean 타입 변수를 이용해

외부에서 생성자에 바로 접근하게 되면 무조건 _creatingSingleton 값은 false
지정되어 에러 메세지를 띄우게 됩니다.

이것 역시 외부의 생성자 접근을 정적 제어변수로 막을 수 있게 되는 것이죠.


iii) 인스턴스 생성 비밀번호를 이용한 방식

package{
     public class Singleton{
    
     private static var _instance:Singleton;
     private static var _secret:Number = Math.random();

     public function Singleton(enforceNumber:Number){
          if (enforceNumber != _secret){
               throw new Error("인스턴스를 생성번호가 일치하지 않습니다.");
          }
     }

     public static function getInstance():Singleton{
          if (_instance == null){
               _instance = new Singleton(_secret);
          }
          return _instance;
     }

     public function doSomething():void{
          trace("do something");
     }
}


_secret 이라는 변수에 랜덤숫자를 발생하여
이 숫자를 인스턴스 생성키로 사용하는 방식입니다.

때문에 외부에서는 이 생성키를 모르므로 생성자에 직접 접근을 할 수가 없게 되겠습니다.

 
iv) 정적 초기화 방식
package{
     public class Singleton{
    
     private static var _instance:Singleton = new Singleton();

     public function Singleton(){
          if (_instance){
               throw new Error("인스턴스를 생성할 수 없습니다.");
          }
     }

     public static function getInstance():Singleton{
          return _instance;
     }

     public function doSomething():void{
          trace("do something");
     }
}


정적 초기화 방식의 싱글톤 패턴입니다.

정적 초기화란 말 그대로
이 클래스의 임의의 구성원(메소드)을 참조할때 정적변수가 초기화되어 인스턴스가
생성되어 지는것을 말합니다.

--->  private static var _instance:Singleton = new Singleton();

이 정적 초기화 싱글톤은 위의 i, ii, iii 방식의 단점을 해결해 줄 수 있습니다.

바로, 멀티 스레드 환경입니다.

위의 i, ii, iii 방식은 인스턴스를 getInstance 메소드로 인스턴스 생성을 요청하지 않으면

불필요한 인스턴스를 생성하지 않는 이점이 있습니다.

하지만 멀티 스레드 환경에서는 싱글톤 인스턴스가 둘 이상 만들어질 수 있다는 단점

있습니다.

쉽게 말해서, 동시에 두개의 스레드가 Singleton.getInstance() 를 호출하게 되면

둘 다 _instance는 null로 인식되어 두개의 인스턴스가 만들어 질 수 있다는 말입니다.


이러한 단점을 해결 할 수 있는 방식이 바로 정적 초기화 방식입니다.



첫번째의 고전적인 방식과 두번째, 세번째의 응용방식 그리고 마지막의 정적 초기화 방식

에 대해서 알아보았습니다.

이제는 이런 싱글톤 패턴을 각자 자신이 알맞은 용도로 사용하는 일만 남았습니다.

저 또한 이것들을 알맞게 사용하기 위해서 많은 시행착오를 거치며 노력할 것입니다.

그럼 수고하세용^^

Comments