Menu
Coddy logo textTech

Has-A関係

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

このコースでは、クラス間で振る舞いを共有するために継承を使ってきました。Dogは「Animalの一種」です。しかし、継承が常に適切な手段とは限りません。

オブジェクト同士が同じ系統樹を共有するのではなく、単に一方のオブジェクトが別のオブジェクトを含む場合があります。これはcompositionと呼ばれ、「has-a」関係をモデル化します。

車について考えてみましょう。車はエンジンの一種ではありません。車にはhas engineがあります。engineは車の内部に存在する独立したobjectです。

local Engine = {}
Engine.__index = Engine

function Engine:new(horsepower)
    local obj = {horsepower = horsepower}
    setmetatable(obj, Engine)
    return obj
end

local Car = {}
Car.__index = Car

function Car:new(model, engine)
    local obj = {
        model = model,
        engine = engine  -- Car内にEngineオブジェクトを格納する
    }
    setmetatable(obj, Car)
    return obj
end

Car を作成するときは、既存の engine object をそのコンストラクタに渡します。

local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)

print(myCar.engine.horsepower)  -- 200

Car は Engine を継承しません。単にその参照を保持します。これにより、両方のクラスは独立したままになります。別の engine に差し替えたり、同じ Engine クラスをオートバイで使用したりできます。

Composition は、継承では得られない柔軟性をもたらします。つまり、オブジェクトを厳格なファミリー階層に固定するのではなく、部品から組み立てるのです。

challenge icon

チャレンジ

簡単

composition を使ってコンピューターシステムを構築しましょう!継承の代わりに、内部に CPU持つComputerを作成し、「has-a」関係を示します。

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

  • CPU.lua:new(brand, speed)コンストラクターを持つCPUクラスを作成し、brand名と速度をGHz単位で保存します。{brand} @ {speed}GHz形式の文字列を返す:getInfo()メソッドを含めます。
  • Computer.lua:new(model, cpu)コンストラクターがmodel名とCPU objectを受け取るComputerクラスを作成します。ComputerはCPU objectを自身の内部に保持します(composition!)。コンピューターのmodelとCPUのinfoを出力する:describe()メソッドを含めます。
  • main.lua:すべてをまとめます!CPUのbrand、CPUの速度、コンピューターのmodelを入力として読み取ります。まずCPU objectを作成し、それをComputerコンストラクターに渡します。最後に、コンピューターの:describe()メソッドを呼び出します。

3つの入力を受け取ります。

  1. CPUのbrand(文字列)
  2. GHz単位のCPU速度(数値)
  3. コンピューターのmodel名(文字列)

出力には、内包されたCPUの情報を含むコンピューターの説明が表示されます。

Model: {model}
CPU: {brand} @ {speed}GHz

たとえば、入力が Intel3.5WorkStation の場合、出力は次のようになります。

Model: WorkStation
CPU: Intel @ 3.5GHz

ComputerがCPUを継承していないことに注目してください。Computerは単にCPUへの参照を保持しています。Computerの:describe()メソッドは、保存されたobjectを通じてCPUのデータにアクセスします。このように、compositionを使うと、より単純な部品から複雑なobjectを構築できます。

自分で試してみよう

-- main.lua
-- コンポジションを使ってすべてをまとめる

local CPU = require('CPU')
local Computer = require('Computer')

-- 入力を読み取る
local cpuBrand = io.read()
local cpuSpeed = tonumber(io.read())
local computerModel = io.read()

-- TODO: ブランドと速度で CPU オブジェクトを作成する

-- TODO: モデルと CPU オブジェクトで Computer オブジェクトを作成する

-- TODO: コンピュータの describe() メソッドを呼び出して出力を表示する
quiz icon腕試し

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

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

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