Menu
Coddy logo textTech

コンポジション

CoddyのRustジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 55/61。

多くのオブジェクト指向言語では、型間で機能を共有するために継承に大きく依存しています。Rustは異なるアプローチ、つまりコンポジションを採用しています。親から振る舞いを継承する代わりに、他の型をフィールドとして含めることで複雑な型を構築します。

車をモデル化することを考えてみましょう。基底 Vehicle クラスを作成してそこから継承するのではなく、より小さく、目的に特化したコンポーネントから Car を構成します。

struct Engine {
    horsepower: u32,
}

struct Wheels {
    count: u8,
    diameter: f32,
}

struct Car {
    engine: Engine,
    wheels: Wheels,
    brand: String,
}

CarEngineWheels を継承していません。持っています。この「has-a」関係がコンポジションです。fields を通じて内部の component にアクセスし、その method を直接呼び出せます。

impl Engine {
    fn start(&self) {
        println!("Engine with {} HP started!", self.horsepower);
    }
}

impl Car {
    fn start(&self) {
        self.engine.start();  // 内部コンポーネントに委譲する
    }
}

このパターンでは、各構造体を単一の責務に集中させられます。エンジンの動作方法を変更する必要がありますか? Engine 構造体だけを変更します。オートバイで Wheels を再利用したいですか?新しい構造体に含めるだけです。コンポジションを使うと、継承が生みがちな密結合なしに柔軟性を得られます。

challenge icon

チャレンジ

簡単

compositionを使ってcomputer systemを構築しましょう!巨大なstructをoneつ作成する代わりに、小さく役割を絞ったcomponent、CPUMemoryからComputerを構成します。各componentは自身を説明する方法を持ち、Computerはそのパーツに処理を委譲します。

コードをthreeつのファイルに整理します:

  • cpu.rs:publicなCPU structをDefineします。このstructには、コア数を表すcores(u32)と、クロック速度を表すspeed_ghz(f32)という2つのpublicなfieldがあります。CPUを{cores}-core @ {speed_ghz}GHzのformatで説明するStringをreturnsするspecs methodを追加します。
  • memory.rs:memory sizeを表すpublicなsize_gb(u32)fieldを持つ、publicなMemory structをDefineします。{size_gb}GB RAMのformatでStringをreturnsするspecs methodを追加します。
  • main.rs:両方のmoduleを取り込み、cpu field(型はCPU)とmemory field(型はMemory)をhasするpublicなComputer structを作成します。Computerにsystem_info methodをImplementし、そのcomponentに処理を委譲して、system全体の仕様をprintします。提供されたinputsを使ってComputerを構築し、その情報を表示します。

system_info methodは次のようにprintする必要があります:

System: {cpu_specs} | {memory_specs}

たとえば、inputsが83.516の場合:

System: 8-core @ 3.5GHz | 16GB RAM

また、inputsが42.832の場合:

System: 4-core @ 2.8GHz | 32GB RAM

threeつのinputを受け取ります:CPU cores(u32としてparse)、CPU speed(f32としてparse)、memory size(u32としてparse)です。

自分で試してみよう

mod cpu;
mod memory;

use cpu::CPU;
use memory::Memory;

// TODO: 2つのパブリックフィールドを持つパブリックなComputer構造体を定義する:
// - cpu: CPU
// - memory: Memory

// TODO: Computerにsystem_infoメソッドを実装する。このメソッドは:
// - cpu.specs()とmemory.specs()に委譲する
// - 次を出力する: System: {cpu_specs} | {memory_specs}

fn main() {
    let mut input = String::new();
    std::io::stdin().read_line(&mut input).expect("Failed to read line");
    let cores: u32 = input.trim().parse().expect("Invalid number");
    
    input.clear();
    std::io::stdin().read_line(&mut input).expect("Failed to read line");
    let speed_ghz: f32 = input.trim().parse().expect("Invalid number");
    
    input.clear();
    std::io::stdin().read_line(&mut input).expect("Failed to read line");
    let size_gb: u32 = input.trim().parse().expect("Invalid number");
    
    // TODO: 解析した入力を使ってCPUとMemoryのインスタンスを作成する
    
    // TODO: CPUとMemoryを持つComputerインスタンスを作成する
    
    // TODO: computerでsystem_info()を呼び出して結果を出力する
}
quiz icon腕試し

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

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

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