【ITニュース解説】Composite Design Pattern in Python....
2025年09月19日に「Dev.to」が公開したITニュース「Composite Design Pattern in Python....」について初心者にもわかりやすく解説しています。
ITニュース概要
PythonのCompositeデザインパターンを紹介する。これは、複数のオブジェクトを階層的に組み合わせ、全体(線や四角形)と部分(点)を同じ方法で扱えるようにする設計パターンだ。複雑な構造を持つオブジェクトをシンプルに管理するため、Pythonコードでその仕組みと使い方を示した。
ITニュース解説
コンポジットデザインパターンは、ソフトウェアの設計において、個々のオブジェクトとそれらを組み合わせた複合オブジェクトを統一的に扱えるようにする構造的なデザインパターンだ。このパターンを利用すると、プログラム内で階層的な構造、例えば木構造のようなものを柔軟に表現し、操作できるようになる。具体的には、個々の部品(「葉」と呼ぶ)と、複数の部品から構成される全体(「枝」と呼ぶ)とを、区別なく扱える共通のインターフェースを提供するのが特徴だ。これにより、コードの複雑さを軽減し、新しい種類の部品や複合オブジェクトが追加されても、既存のコードに大きな変更を加えずに対応できる拡張性が生まれる。
提示されたPythonのコードは、このコンポジットデザインパターンを図形の描画システムに適用した具体的な例である。まず、すべての図形が共通して持つべき基本的な機能や振る舞いを定義するために、「Shape」という抽象基底クラスが定義されている。このクラスはabcモジュールからのABCとabstractmethodを使っており、これはShapeクラス自体を直接インスタンス化するのではなく、他のクラスに継承されて具体的な機能が実装されることを示している。Shapeクラスには、add、remove、displayという三つの抽象メソッドが定義されており、これらを継承するすべてのクラスはこれらのメソッドを必ず実装する必要がある。また、parentという属性を持ち、これが図形が属する親のオブジェクトへの参照を保持する。
次に、「CompositeShape」というクラスが登場する。これは、複数のShapeオブジェクトを内部に含むことができる「複合図形」を表す。CompositeShapeはShapeクラスを継承しているため、add、remove、displayという共通のインターフェースを持つ。このクラスの内部ではlistofShapesというリストが定義されており、自身の子となる図形オブジェクトをこのリストに格納する。addメソッドが呼び出されると、渡された図形をlistofShapesに追加し、その子図形に対して自身のsetparentメソッドを呼び出して親子関係を確立する。removeメソッドはリストから子図形を削除し、子図形の親参照を解除する。displayメソッド自体はここでは具体的な処理を持たず、継承する具象クラスにその実装を委ねる形となっている。
続いて、「Point」クラスがある。これは特定のX座標とY座標を持つ「点」を表す最も基本的な図形であり、コンポジットデザインパターンにおける「葉」の役割を果たす。PointもShapeを継承しているが、Pointはそれ以上内部に子図形を持つことができないため、addやremoveメソッドが呼び出された際には、「これは葉クラスであり、追加・削除はできない」というメッセージを出力するようになっている。displayメソッドは、そのPointオブジェクトが持つX座標とY座標の値をシンプルに表示する。
さらに、具体的な複合図形として「Line」と「Quadrilateral」のクラスが実装されている。これらのクラスはどちらもCompositeShapeを継承しており、それぞれ複数のPointオブジェクトを組み合わせて「線」と「四角形」を表現する。例えば、Lineクラスのコンストラクタは二つのPointオブジェクトを受け取り、それらをCompositeShapeのaddメソッドを使って自身のlistofShapesに追加する。Quadrilateralクラスも同様に四つのPointオブジェクトを受け取り、自身のリストに追加する。これらのクラスにおけるdisplayメソッドの動作が重要だ。LineやQuadrilateralのdisplayメソッドは、自身が内部に持つlistofShapesの要素(この例ではPointオブジェクト)を一つずつループ処理し、それぞれの要素に対してdisplayメソッドを呼び出す。これにより、線や四角形を構成するすべての点が適切に表示され、LineやQuadrilateral自身もあたかも一つの「Shape」として振る舞うことができる。
コードの末尾にあるif __name__ == '__main__':ブロックは、これまでに定義されたクラスが実際にどのように利用されるかを示す実行例だ。まず、四つのPointオブジェクト(p1、p2、p3、p4)が作成される。p1.add(p2)という呼び出しが行われると、Pointクラスのaddメソッドが実行され、「これは葉クラスであり、追加できない」という出力が表示される。これはPointが最も基本的な図形で、これ以上子を持つことができないという性質を明確に示している。
その後、p1とp2を構成要素とする「L1」というLineオブジェクト、およびp1、p2、p3、p4を構成要素とする「q1」というQuadrilateralオブジェクトがそれぞれ作成される。そして、p1.display()、p2.display()、L1.display()、q1.display()と、さまざまな種類の図形オブジェクトに対して同じdisplayメソッドが呼び出されている点に注目が必要だ。p1やp2といった個々の点だけでなく、L1が表現する線、q1が表現する四角形も、すべて同じdisplayメソッドの呼び出しを通じて、その内容がコンソールに出力される。これがコンポジットデザインパターンの最大の利点であり、個々のオブジェクトも複合オブジェクトも、共通のShapeインターフェースを通じて統一的に扱えることを実証している。
このパターンを採用することで、クライアント側のコードは、操作している対象が個別の図形なのか、それとも複数の図形をまとめたグループなのかを区別する必要がなくなる。すべてのShapeオブジェクトに対して同じメソッドを呼び出すだけで、期待通りの振る舞いが得られるため、コードはシンプルになり、理解しやすくなる。また、将来的に新しい種類の図形(例えば円、三角形、あるいは図形をさらに階層的にまとめるグループ機能など)を追加する必要が生じた場合でも、Shapeインターフェースを実装し、コンポジットパターンに従ってクラスを設計するだけで、既存のクライアントコードにほとんど手を加えることなく、容易にシステムを拡張できるようになる。このような柔軟性と保守性の高さは、システムエンジニアが効率的かつ堅牢なソフトウェアを開発する上で非常に重要な考え方となる。