コンポジションを通じた責任の委譲は、あるクラスのオブジェクトが別のクラス(またはクラス)のオブジェクトを含み、それらを使用して自らのロジックの一部を実装するプログラミングの実践であり、インターフェースを継承するのではありません。
初期のOOPは継承に重点を置いていましたが、時間とともに実践は示しました:コンポジションはしばしば、柔軟性、拡張性を提供し、コンポーネント間の結合度を低下させます。
継承はオブジェクトを厳密に結びつけます:基底クラスの変更はすべての子クラスに影響を与え、階層が複雑になり、アーキテクチャの脆弱性が生じます。コンポジションはこれらの問題を解消し、より信頼性が高く、保守しやすいシステムを構築できるようにします。
C++では、委譲はあるクラスのオブジェクトを別のクラスのメンバーとして包含することによって実現されます。ラッパークラスでは、内包されたオブジェクトのメソッドが呼び出されます。
コードの例:
class Logger { public: void log(const std::string& msg) { std::cout << msg << std::endl; } }; class FileProcessor { Logger logger; // コンポジション public: void process(const std::string& filename) { logger.log("Processing file: " + filename); // ... } };
主な特徴:
コンポジションは継承を完全に置き換えることができますか?
いいえ、継承は「is-a」(は〜である)関係が必要な場合に、コンポジションは「has-a」(を持っている)場合に必要です。例えば、ButtonはWidgetを継承していますが、CarはEngineを「持っています」(コンポジション)。
コンポジションで委譲されたメソッドの動作を変更できますか?
はい、委譲メソッドは元のクラスに触れずに適応できます。また、委譲先のオブジェクトを動的に変更することも可能です(例えば、ポインタやユニークポインタを介して)。
コンポジションは継承よりも遅いですか?
いいえ、ほとんどの場合、パフォーマンスの違いはありません。時には継承が仮想呼び出し(vtable)のコストを追加しますが、コンポジションは単にオブジェクトのサイズです。
プロジェクトでは、すべてのダイアログウィンドウが共通のDialogWindowを継承していました。新しいビジネスロジックの追加がすべての子クラスに機能しないコードを引き起こしました。
利点:
欠点:
共通の機能は、各ダイアログにコンポジションを通じて導入される別のクラス(ログ記録、バリデーション)に移されました。
利点:
欠点: