概要
木構造は階層的なデータを表現するのに非常に有効だ。Compositeパターンは、この法則をオブジェクト指向プログラミングの世界にも適用したもので、階層的な関係を持つオブジェクトをツリー状に整理する。
注意すべき点は、Compositeパターンでは、コンポジットオブジェクト(ツリー)とプリミティブオブジェクト(リーフノード)は、適度にインターフェースを統一する必要がある点だ。
ユーザーは、ノードのタイプ(コンポジットまたはリーフ)を知らなくても、ノードに対していくつかの操作を行うことができるはずである。他の操作は、実際にノードタイプの知識を必要とするかもしれない。インターフェイスのどこまでを統一するかは、ユースケースによって異なる。
以下の実装例では、シンプルで読みやすいデータを保存するユースケースを示す。
DataObjectクラスは、コンポジットノードとリーフノードの間の共通インターフェースを定義する。コンポジットデータタイプとリーフデータタイプの両方がこれを継承している。以下の実装では、親ノードへのポインタは不要だが、ユースケースに応じて追加もできる。
DataNode クラス はリーフノードを表し、コンポジットではないデータを含む。
DataComposite クラス はツリーを表し、子ノードを追加・削除するためのインターフェースを備えている。DataCompositeオブジェクトとDataNodeオブジェクトでは、data引数の扱いが異なることに注意が必要。コンポジットオブジェクトに渡されるデータは、コンポジットセットに関するメタデータとして設定される。ユーザは、データを渡す際にこのことを知る必要はない。
DataCompositeクラスのread()関数は、子クラスでメソッドの動作が異なっていても、DataObjectのすべての子クラスでreadメソッドを呼び出し、希望の応答を得られる。
実装例
class DataObject():
def read(self): pass
class DataNode(DataObject):
def __init__(self, data):
self._data = data
def read(self):
print("Node Data: ", self._data)
class DataComposite(DataObject):
def __init__(self, data):
self._meta_data = data
self.sub_objects = []
def read(self):
print("Data Composite For: ", self._meta_data)
for data_object in self.sub_objects:
data_object.read()
def add(self, data_object):
self.sub_objects.append(data_object)
def remove(self, data_object):
self.sub_objects.remove(data_object)
if __name__ == "__main__":
node_11 = DataNode("1-1")
node_12 = DataNode("1-2")
node_21 = DataNode("2-1")
tree_1 = DataComposite("1")
tree_2 = DataComposite("2")
tree_root = DataComposite("root")
tree_1.add(node_11)
tree_1.add(node_12)
tree_2.add(node_21)
tree_root.add(tree_1)
tree_root.add(tree_2)
tree_root.read()