ActionScript 3 (Flash/AIR): GoF デザインパターン – Flyweight

要はオブジェクトプール。同じものを共有して無駄を省きます。

Singleton に近いデザインパターンです。 getFlywight を呼び出したときに欲しいインスタンスが生成されていなかった場合は生成し、プールに貯めておき、return で返します。

プールに残っていたら新たなインスタンスを new せずにプールから取り出して return します。Singleton の getInstance は Singleton クラスのインスタンスを返していましたが、Flyweight では自身でないオブジェクトを返します。

インスタンスを作るときの new は重い処理なので、オブジェクトを作ったらオブジェクトプールに貯めておいて、必要なときに取り出したりする方が処理のパフォーマンスをあげる事が出来ます。

ActionScript: Flyweight.as

package jp.feb19.gof.flyweight
{
	public class Flyweight
	{
		protected var _circleInstrinsic:Object;
		
		public function Flyweight()
		{
			_circleInstrinsic = new Object();
			_circleInstrinsic["red"] = new ConcreteFlyweight(0xff0000);
			_circleInstrinsic["yellow"] = new ConcreteFlyweight(0xffff00);
		}
		
		public function getInstrinsic(key:String, ...rest):FlyweightCircle
		{
			switch(_circleInstrinsic[key] != undefined)
			{
				case true:
					break;
				case false:
					_circleInstrinsic[key] = rest[0];
					break;
			}
			
			return _circleInstrinsic[key];
		}
	}
}

Flyweight に格納するクラス。Flyweight パターンとは関係ないですがなんとなく拡張し易いように FlyweightCircle と ConcreteFlyweight で分離してます。

ActionScript: FlyweightCircle.as

package jp.feb19.gof.flyweight
{
	import flash.display.Sprite;
	
	public class FlyweightCircle extends Sprite
	{
		public function FlyweightCircle()
		{
			super();
		}
		
		public function drawCircle(x:int, y:int, r:uint):void
		{
			
		}
	}
}

ActionScript: ConcreteFlyweight.as

package jp.feb19.gof.flyweight
{
	public class ConcreteFlyweight extends FlyweightCircle
	{
		private var _color:uint;
		
		public function ConcreteFlyweight(color:uint)
		{
			_color = color;
		}
		
		override public function drawCircle(x:int, y:int, r:uint):void
		{
			graphics.beginFill(_color);
			graphics.drawCircle(x, y, r);
			graphics.endFill();
		}
	}
}

テストクラス

ActionScript: FlyweightTest.as

{
	import flash.display.Sprite;
	
	public class FlyweightTest extends Sprite
	{
		public function FlyweightTest()
		{
			super();
			
			var flyweight:Flyweight = new Flyweight();
			
			var circle1:FlyweightCircle = flyweight.getInstrinsic("red");
			circle1.drawCircle(Math.random() * 500, Math.random() * 300, Math.random() * 100);
			addChild(circle1);
			
			var circle2:FlyweightCircle = flyweight.getInstrinsic("red");
			circle2.drawCircle(Math.random() * 500, Math.random() * 300, Math.random() * 100);
			addChild(circle2);
			
			var circle3:FlyweightCircle = flyweight.getInstrinsic("yellow");
			circle3.drawCircle(Math.random() * 500, Math.random() * 300, Math.random() * 100);
			addChild(circle3);
			
			var circle4:FlyweightCircle = flyweight.getInstrinsic("yellow");
			circle4.drawCircle(Math.random() * 500, Math.random() * 300, Math.random() * 100);
			addChild(circle4);
			
			var circle5:FlyweightCircle = flyweight.getInstrinsic("blue", new ConcreteFlyweight(0x00ff00));
			circle5.drawCircle(Math.random() * 500, Math.random() * 300, Math.random() * 100);
			addChild(circle5);
		}
	}
}

オブジェクトプールに貯めておく事のメリットとしてさらに、貯めてあるオブジェクトを一気に削除することができるので、自分はサウンド系のインスタンスやイベントリスナーをオブジェクトプールに入れておいて、メモリを綺麗にするときに Flyweight に用意した clean() メソッドを呼び出す事でプールを掃除しています。