Composite

Compositeパターンは、自身のコントロール下にある複雑な配置をしたオブジェクト群を操作したい場合に有効な手法である。 例としてよく引き合いに出されるのはツリー構造のオブジェクト群で、 Compositeパターンはツリーの幹から末端へかけて情報が伝達していくような仕組みとして説明されることが多い。 ここでも例にもれず、以下のような簡単なツリー構造のオブジェクトを考えよう。 このようなツリー構造をしたオブジェクト群のトップレベルオブジェクト(ルート)から全てのオブジェクトに情報を伝達させてみる。 自身の子オブジェクトに情報を伝達させる簡単な方法としては以下のようなアルゴリズムが考えられるだろう。
  1. 子オブジェクトがリーフであった場合そこで伝達終了。
  2. 子オブジェクトがノードであった場合、そこからさらに子オブジェクト(孫オブジェクト)に伝達していく。

この場合、子オブジェクトがリーフの場合とノードの場合で伝達方法が異なる点に注意していただきたい。 伝達対象の違いによる分岐がアルゴリズムに生じる分、操作アルゴリズムが複雑になることが予想されるわけである。 一方、Compositeパターンはリーフとノードを区別せずに全ての子オブジェクトを操作することができるため、 より軽量な操作アルゴリズムをクライアントに提供することができる。 ポイントはリーフとノードに同じクラスを継承させているという点である。 このように異なるクラスを同じクラスとして扱い、両者の区別をさせない(できない)状態を "Erich Gamma - Design Patterns"ではTransparencyと呼んでおり、デザインパターンではしばしば重要な概念とされている。

Transparencyを前面に扱っているものとしてDecoratorパターンが挙げられる。 Decoratorパターンでは装飾されるクラスと装飾を行うクラスを区別させないような巧妙な手口が用いられており、 クライアントにシンプルなインターフェイスを提供している。

Example

ここでは"William Sanders - ActionScript 3.0 Design Patterns"の例題を参考にした。 それをさらに簡略化しCompositeパターンの特徴だけを残したものを挙げてある (実際にはノードの削除もCompositeパターンの範疇になる)。 適当に子ノードを追加し、ルートオブジェクトから情報を伝達させている。 重要なポイントはリーフ(Leaf.as)とノード(Node.as)ともに同じクラス(ANode.as)を継承している点である。 そのため情報伝達の際、子オブジェクトの種類による場合分けが生じず、シンプルなクラス構造が実現している。 (両者の違いはnotifyメソッドのoverrideによるもの)
//main.as
package {
	import flash.display.Sprite;
	import composite.ANode;
	import composite.Node;
	import composite.Leaf;
 
	public class main extends Sprite{
		public function main(){
			var root:ANode = new Node("root");
			var node0:ANode = new Node("node0");
			var node1:ANode = new Node("node1");
			var leaf0:ANode = new Leaf("leaf0");
			var leaf1:ANode = new Leaf("leaf1");
			var leaf2:ANode = new Leaf("leaf2");
			var leaf3:ANode = new Leaf("leaf3");
			var leaf4:ANode = new Leaf("leaf4");
 
			root.addChild(node0);
			root.addChild(node1);
			node0.addChild(leaf0);
			node0.addChild(leaf1);
			node1.addChild(leaf2);
			node1.addChild(leaf3);
			node1.addChild(leaf4);
			root.notify("hello");
		}
	}
}
//main.as output
root: hello
node0: hello
leaf0: hello
leaf1: hello
node1: hello
leaf2: hello
leaf3: hello
leaf4: hello
//ANode (Abstract)
package composite{
	import flash.errors.IllegalOperationError;
 
	public class ANode{
		protected var _name:String;
		public function get name():String{
			return _name;
		}
 
		/**
		 * Constructor
		 */ 
		public function ANode(name:String){
			_name = name;
		}
 
		public function addChild(c:ANode):void{
			throw new IllegalOperationError("addChild not supported: " 
			+ Object(this).constructor);
		}
 
		public function notify(message:String):void{
			throw new IllegalOperationError("notify not supported: " 
			+ Object(this).constructor);
		}
	}
}
//Node.as
package composite{
	public class Node extends ANode{
		protected var _children:Array;
 
		/**
		 * Constructor
		 */ 
		public function Node(name:String){
			super(name);
			_children = new Array();
		}
 
		override public function addChild(c:ANode):void{
			_children.push(c);
		}
 
		override public function notify(message:String):void{
			trace(_name + ": " + message);
			for each (var c:ANode in _children){
				c.notify(message);
			}
		}
	}
}
//Leaf.as
package composite{
	public class Leaf extends ANode{
 
		/**
		 * Constructor
		 */ 
		public function Leaf(name:String){
			super(name);
		}
 
		override public function notify(message:String):void{
			trace(_name + ": " + message);
		}
	}
}
© 2009 7614.org