コンポジションと継承
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 76/104。
クラス間の関係を設計する際には、主に2つの選択肢があります。継承(「is-a」)とcomposition(「has-a」)です。適切なアプローチを選ぶことは、コードの柔軟性と保守性に大きな影響を与えます。
継承はクラス間に強い結合を生み出します。Vehicleを継承するCarは、その関係に恒久的に束縛されます。
class Vehicle {
public:
virtual void start() { std::cout << "Starting vehicle\n"; }
};
class Car : public Vehicle {
public:
void start() override { std::cout << "Starting car\n"; }
};Compositionはオブジェクトをメンバーとして埋め込み、より柔軟な関係を作成します。Carはエンジンそのものではなく、Engineをhasします:
class Engine {
public:
void start() { std::cout << "Engine running\n"; }
};
class Car {
private:
Engine engine; // Car は Engine を持つ
public:
void start() { engine.start(); }
};composition の主な利点は柔軟性です。コンポーネントを簡単に交換したり、実行時に動作を変更したり、堅 rigid なクラス階層の制約なしに複数の機能を組み合わせたりできます。現代の C++ 設計では、ほとんどの場合、継承よりも composition が優先されます。
真正の「is-a」関係があり、ポリモーフィズムが必要な場合は、継承を使用します。機能を再利用したい場合、または関係を「has-a」や「uses-a」と表現する方が適切な場合は、コンポジションを使用します。
チャレンジ
簡単継承ではなく composition をいつ使うべきかを示す、computer system simulator を作りましょう。has components(composition)という computer をモデル化し、component being ではない形にすることで、実行時に部品を交換できる composition の柔軟性を示します。
コードを3つのファイルに分けて構成します。
Components.h:computer が contain できる個々の component を定義します。private な
std::string modelとint coresを持つCPUclass を Create します。両方の値を受け取る constructor、CPU [model] processing with [cores] coresを出力するprocess()method、およびgetModel()method を用意します。private な
int sizeGBを持つMemoryclass を Create します。constructor、Loading data into [sizeGB]GB RAMを出力するload()method、およびgetSize()method を用意します。private な
std::string type("SSD" や "HDD" など)とint capacityGBを持つStorageclass を Create します。constructor、Reading from [capacityGB]GB [type]を出力するread()method、および両方のフィールド用の getter method を用意します。Computer.h:composition を使用して component を組み合わせるComputerclass を定義します。Computerは、private な member object としてCPU、Memory、Storageを持つ必要があります。constructor initialization list を使用して、parameters からこれらの component を初期化します。次の method を Implement します。
boot():Booting computer...を出力し、続けて memory に対してload()、storage に対してread()、CPU に対してprocess()を呼び出します。それぞれ別の行に出力します。specs():computer の仕様を次の形式で出力します。Computer Specs: - CPU: [model] ([cores] cores) - RAM: [size]GB - Storage: [capacity]GB [type]
main.cpp:6つの入力を読み取ります(それぞれ別の行に入力されます)。- CPU model name(string)
- CPU cores の数(integer)
- GB 単位の Memory size(integer)
- Storage type(string、例: "SSD")
- GB 単位の Storage capacity(integer)
- command:"boot" または "specs" のいずれか
component object を Create し、続けて composition を使用して
Computerを Create します。command に基づいて、boot()またはspecs()のいずれかを call します。
たとえば、入力が Intel i7、8、16、SSD、512、boot の場合:
Booting computer...
Loading data into 16GB RAM
Reading from 512GB SSD
CPU Intel i7 processing with 8 cores入力が AMD Ryzen、6、32、HDD、1000、specs の場合:
Computer Specs:
- CPU: AMD Ryzen (6 cores)
- RAM: 32GB
- Storage: 1000GB HDDComputer class がどの component からも継承していないことに注目してください。component を contains しています。この "has-a" relationship により、異なる component の組み合わせを持つ computer を簡単に Create できます。また、setter method を追加すれば、後から component を交換することもできます。この柔軟性が、inheritance に対する composition の主な利点です。
自分で試してみよう
#include <iostream>
#include <string>
#include "Computer.h"
using namespace std;
int main() {
// 入力を読み取る
string cpuModel;
getline(cin, cpuModel); // CPUモデル名(スペースを含む場合あり)
int cpuCores;
cin >> cpuCores; // CPUコア数
int memorySize;
cin >> memorySize; // メモリサイズ(GB)
string storageType;
cin >> storageType; // ストレージタイプ(例: "SSD")
int storageCapacity;
cin >> storageCapacity; // ストレージ容量(GB)
string command;
cin >> command; // コマンド: "boot" または "specs"
// TODO: コンポーネントオブジェクト(CPU、Memory、Storage)を作成する
// TODO: コンポジションを使用してComputerオブジェクトを作成する
// TODO: コマンドに基づいて、boot() または specs() を呼び出す
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス自分で練習してみよう: C++オンラインコンパイラ