Menu
Coddy logo textTech

コンポジションと継承

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はエンジンそのものではなく、Enginehasします:

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」と表現する方が適切な場合は、コンポジションを使用します。

challenge icon

チャレンジ

簡単

継承ではなく composition をいつ使うべきかを示す、computer system simulator を作りましょう。has components(composition)という computer をモデル化し、component being ではない形にすることで、実行時に部品を交換できる composition の柔軟性を示します。

コードを3つのファイルに分けて構成します。

  • Components.h:computer が contain できる個々の component を定義します。

    private な std::string modelint cores を持つ CPU class を Create します。両方の値を受け取る constructor、CPU [model] processing with [cores] cores を出力する process() method、および getModel() method を用意します。

    private な int sizeGB を持つ Memory class を Create します。constructor、Loading data into [sizeGB]GB RAM を出力する load() method、および getSize() method を用意します。

    private な std::string type("SSD" や "HDD" など)と int capacityGB を持つ Storage class を Create します。constructor、Reading from [capacityGB]GB [type] を出力する read() method、および両方のフィールド用の getter method を用意します。

  • Computer.h:composition を使用して component を組み合わせる Computer class を定義します。

    Computer は、private な member object として CPUMemoryStorage を持つ必要があります。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つの入力を読み取ります(それぞれ別の行に入力されます)。
    1. CPU model name(string)
    2. CPU cores の数(integer)
    3. GB 単位の Memory size(integer)
    4. Storage type(string、例: "SSD")
    5. GB 単位の Storage capacity(integer)
    6. command:"boot" または "specs" のいずれか

    component object を Create し、続けて composition を使用して Computer を Create します。command に基づいて、boot() または specs() のいずれかを call します。

たとえば、入力が Intel i7816SSD512boot の場合:

Booting computer...
Loading data into 16GB RAM
Reading from 512GB SSD
CPU Intel i7 processing with 8 cores

入力が AMD Ryzen632HDD1000specs の場合:

Computer Specs:
- CPU: AMD Ryzen (6 cores)
- RAM: 32GB
- Storage: 1000GB HDD

Computer 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;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C++オンラインコンパイラ