デザインパターン シングルトンパターン

2011.02.22kickbaseActionScript3.0


前回に引き続きデザインパターン(以下デザパタと略)の話題を。今回はシングルトンパターンについて解説をします。シングルトンパターンは非常に使いやすく、構造もそれほど難しくないのでデザパタ入門としておすすめです。しかしデザパタには全般に通じる注意点があります。それは使い過ぎてしまうことです。デザパタを勉強をしていくとその優れたロジックに感銘を受け、ともすれば特に使う必要もない場面で使ってしまいがちです。

今回説明するシングルトンパターンは僕も良く使いますが、元々はProgression3の時代にデータを一元管理するために勉強したパターンでした。しかしProgression4の「idを使った強力な参照機能」や「Resourceクラス」を利用するようになり、このパターンはプログレとはあまり併用しなくなってきました。デザパタを勉強することで得られることは非常に多いですが、不必要なパターンの乱用はコードを複雑にします。「このパターンは今回は必要ないかも」という目線で設計するほうが、個人的には良いと思います。(とは言え本パターンはトップクラスの使用頻度を誇るので、今回はFDのテンプレートファイルもサンプルに含めておきました)

シングルトンパターン : クラスがインスタンスをひとつしか持たないことを保障してくれるパターン

00 : 通常の実装(オブジェクト指向性プログラム)
何の変哲もない普通の実装です。クラスは設計図に、インスタンスはそれぞれ設計図を元に作られたモノと捉えることが出来ます。今回はCarという車についてのクラスを作成しました。これから作られるインスタンスは、プロパティとして車体のカラーとガソリン残量を持っています。メソッドとしては、走ったり、給油したり、車体のカラーを塗り替えたりといったものを定義しました。ここで最も重要な点は、インスタンスは各々独立した状態を保持しておけるという点です。下図のように3台の車があった場合、色もガソリン残量も各々違いますね。

シングルトンパターンを使用すると、下図の様になります。インスタンスを2個,3個,4個.…と、次々生成しても、中身は一番最初に作ったインスタンス1を参照するようになります。2個目以降のインスタンスは、ちょうどWindowsでいうところのショートカットアイコン(Macでいうところのエイリアス)のようなものと捉えるとイメージしやすいかと思います。ショートカットですから、当然全ての機能を使うことができますし、同じものを参照しているので全てのプロパティが等しくなります。

この仕組みを使うことで「いつ、どこで」インスタンス化をしても、全て同じインスタンスにアクセスすることが出来るようになります。パスワードやIDの管理をするときに便利ですね。いつどこで情報が変更されても、大元のひとつが変更されるのでどこでも常に最新の情報を保持してくれています。また、ロードしたデータを一括して保持するクラスに使用してもいいでしょう。一回ロード完了し、データを格納しておけば、どこからでもデータを取り出せるようになります。

以下、コードを見ながら実際の構造を説明していきます。

01 : シングルトンパターンの構造
シングルトンパターンの真髄は、インスタンス化をコントールするということにつきます。その仕組みは以下の通りです。

▼直接newできないようにする
new演算子を使ってインスタンス化する際、引数をひとつ渡すようにします。そしてその引数のキャストをInternalにしておきます。このInternalクラスというのはSingletonクラスの一番下あたりに記載してあるクラスでインナークラスと呼ばれるものです。インナークラスは記載してあるファイル外から使用することが出来ないという特性を持っているので、外部からInternalクラスのインスタンスを生成することは出来ません。それを回避しようとしてnullを引数とした場合にも、エラーが返るよう実装しておきます。このように間違ってnew演算子を使ってインスタンス化しようとしてもできない仕組みを作っておきます。

コンストラクタ

public function Singleton(internally:Internal)
		{
			if (internally == null)
			{
				throw new Error("コンストラクタの直接呼び出しは禁止されています。getInstance()メソッドを使ってインスタンス化してください。");
			}
		}

▼インスタンス化はgetInstanceメソッドで
では、どのようにインスタンス化をするかというとクラスメソッドであるgetInstanceメソッドを使います。クラスメソッドなのでインスタンス化せず使えます。

Main

var singleton1:Singleton = Singleton.getInstance();

▼getInstanceメソッド内部では何が起こっているのか
初回実行時には_instanceはnullです。なのでif文内のステートメントが実行されインスタンスが生成されます。そして出来立てのインスタンスを返します。2回目以降は既にインスタンスが存在しているので、if文は実行されず(つまりインスタンス化されず)既存のインスタンスを返します。

この「無ければ作って返す」「既にあればそれを返す」という処理が、シングルトンパターンの正体です。

既にあればそれを返す事で2個以上のインスタンス化を防ぎ、あたかもショートカットアイコンのような振る舞いをしてくれるのです。
※この処理は他のデザパタでもよく登場します。下記参考サイトでも紹介していますが、しっぽ流デザインパターン講座が非常に分りやすいのでご参照ください。

getInstanceメソッド

static public function getInstance():Singleton
		{
			//_instanceが厳密にnullの時(つまりはじめの1回目)だけインスタンスを生成します
			if (_instance === null)
			{
				_instance = new Singleton(new Internal());
			}
			return _instance;//2回目以降は、既に生成されているインスタンスを返します
		}

▼上記をまとめるとSingletonクラスが出来上がります

Singleton

package  
{
	public class Singleton 
	{
		//クラスメンバとしてSingletonクラスのインスタンスを保持します。まだインスタンス化はしません
		static private var _instance:Singleton;
		
		static public function getInstance():Singleton
		{
			//_instanceが厳密にnullの時(つまりはじめの1回目)だけインスタンスを生成します
			if (_instance === null)
			{
				_instance = new Singleton(new Internal());
			}
			return _instance;//2回目以降は、既に生成されているインスタンスを返します
		}
		
		/*--------------------------------------------------------------------------
		 * コンストラクタ
		 *---------------------------------------------------------------------*//**
		 * <p>コンストラクタの直接呼び出しは禁止されています。getInstance()メソッドを使ってインスタンス化してください。</p> 
		 */
		public function Singleton(internally:Internal)
		{
			//Internalクラスのインスタンスがnullならば、「直接newされた」ということなのでエラーを返します
			if (internally == null)
			{
				throw new Error("コンストラクタの直接呼び出しは禁止されています。getInstance()メソッドを使ってインスタンス化してください。");
			}
			
			//初期化
			trace("インスタンスが生成されました");
		}
	}
}

/*
 * インナークラスとしてInternalクラスを作成します。(JAVAのインナークラスとは挙動が異なります)
 * package外に生成したクラス(インナークラス)は、宣言したasファイル内でのみ使用できます。
 * 
 * 逆に言うと外部からInternalクラスのインスタンスを生成することが出来ないので、内部からの
 * アクセスかどうかを判定するキーとすることが可能になります。
*/
class Internal {
	
}

02 : 実例 – シングルトンCarクラスを使って各プロパティの挙動を確認する
簡単な実例を用意しました。ポイントはgetInstanceメソッドに引数を渡す仕様にした点です。メソッドがいくつかありますが、00で作成したCarクラスと同じものです。SingletonCarクラスと見比べながら、どのようにシングルトンパターンを組み込んだかを確認して下さい。長文になるのでコードの掲載は割愛しますが、サンプルには全クラスが含まれています。コメントも多く入れてあるので、そちらも合わせて参照してください。

03 : おまけのFDテンプレート(Singleton.as.fdt)
下記ディレクトリに保存することでテンプレートとして使用することが出来るようになります。(WindowsXP環境)
右クリック > 新規作成…で出てくるようになると思います。ご使用の際はEntryPointや改行位置など、ご自由に改変してください。

C:\Documents and Settings\ユーザー名\Local Settings\Application Data\FlashDevelop\Templates\ProjectFiles\AS3Project

Singleton.as.fdt

package $(Package) $(CSLB){
	public class $(FileName) $(CSLB){
		static private var _instance:$(FileName);
		$(EntryPoint)
		
		static public function getInstance():$(FileName)
		{
			if (_instance === null) _instance = new $(FileName)(new Internal());
			return _instance;
		}
		
		/*--------------------------------------------------------------------------
		 * コンストラクタ
		 *---------------------------------------------------------------------*//**
		 * <p>コンストラクタの直接呼び出しは禁止されています。getInstance()メソッドを使ってインスタンス化してください。</p> 
		 */
		public function $(FileName)(internally:Internal) $(CSLB){
			if (internally == null)
			{
				throw new Error("コンストラクタの直接呼び出しは禁止されています。getInstance()メソッドを使ってインスタンス化してください。");
			}
			//initialize
			
		}
	}
}

class Internal {
	
}

サンプルのダウンロード

参考サイト

ActionScript3.0でSingletonパターンを実装する
c9日記 -カタヤマンがプログラマチックに今日もコードアシスト [AS3][Flex2]インナークラス
しっぽ流デザインパターン講座(F-site講演資料)


ページトップへ