ファクトリ関数
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 62/70。
このコースを通じて、Circle:new()やRectangle:new()などのコンストラクタを直接呼び出すことでオブジェクトを作成してきました。しかし、呼び出し側のコードがどの特定のクラスを使用するかを知る必要なしに、何らかの条件に基づいて異なるタイプのオブジェクトを作成したい場合はどうすればよいでしょうか?
ここでFactoryパターンの出番です。ファクトリとは、単にオブジェクトを作成して返す関数のことです。主な利点は、呼び出し側がオブジェクト作成の詳細を知る必要がないことです。呼び出し側は単に、必要なものをファクトリに要求するだけです。
local function createShape(shapeType)
if shapeType == "circle" then
return Circle:new(10)
elseif shapeType == "square" then
return Square:new(5)
end
end
-- 呼び出し側は Circle クラスや Square クラスについて知る必要はありません
local myShape = createShape("circle")
print(myShape:area())
ファクトリ関数は文字列の引数を受け取り、どのクラスをインスタンス化するかを決定します。これにより、オブジェクト生成ロジックが1か所に集約されます。後で新しい図形の種類を追加する場合、ファクトリを修正するだけで済み、図形を生成するすべてのコードを修正する必要はありません。
Factory(ファクトリ)は、オブジェクトの作成に複雑なセットアップが必要な場合、実装の詳細を隠蔽したい場合、またはオブジェクトの正確な型がユーザー入力や設定ファイルなどの実行時の条件に依存する場合に、特に有用です。
チャレンジ
簡単ペット工場(Pet Factory)を作ってみましょう!各クラスのコンストラクタを直接呼び出してペットを作成する代わりに、単純な文字列の引数に基づいて適切なタイプのペットを生成するファクトリ関数を作成します。
コードは以下の4つのファイルに分けて構成します:
Dog.lua: ペットの名前を保存する:new(name)コンストラクタを持つ Dog クラスを作成します。"{name} says: Woof!"({name} は犬の名前)という文字列を返す:speak()メソッドを追加してください。Cat.lua: 同じ構造を持つ Cat クラスを作成します。:new(name)コンストラクタと、"{name} says: Meow!"を返す:speak()メソッドを実装してください。PetFactory.lua: ここが Factory パターンの見せ所です!ペットのタイプを示す文字列("dog"または"cat")とペットの名前の2つの引数を受け取るcreatePet(petType, name)関数を作成します。この関数は適切なペットオブジェクトを返す必要があります。タイプが"dog"の場合は新しい Dog を、"cat"の場合は新しい Cat を返してください。main.lua: PetFactory モジュールを require します。ペットのタイプとペットの名前の2つの入力を読み取ります。ファクトリ関数を使用してペットを作成し、その:speak()メソッドを呼び出して結果を出力してください。
Factory パターンの素晴らしい点は、メインのコードが Dog や Cat を直接 require する必要がないことです。メインコードはファクトリのことだけを知っていればよく、どのクラスをインスタンス化するかという詳細はすべてファクトリが処理します。
2つの入力を受け取ります:
- ペットのタイプ(
"dog"または"cat") - ペットの名前(文字列)
例えば、入力が dog と Buddy の場合、出力は以下のようになります:
Buddy says: Woof!入力が cat と Whiskers の場合、出力は以下のようになります:
Whiskers says: Meow!チートシート
Factoryパターンは、与えられたパラメータに基づいて関数がオブジェクトを作成して返すデザインパターンです。呼び出し側は、どの特定のクラスがインスタンス化されているかを知る必要はありません。Factory(工場)がその決定を処理します。
Factory関数は、オブジェクト作成のロジックを一箇所に集約します:
local function createShape(shapeType)
if shapeType == "circle" then
return Circle:new(10)
elseif shapeType == "square" then
return Square:new(5)
end
end
local myShape = createShape("circle")
print(myShape:area())
Factoryパターンの利点:
- 呼び出し側から実装の詳細を隠蔽する
- オブジェクトの作成を一箇所に集約する
- 新しい型の追加が容易になる(Factoryのみを修正すればよい)
- オブジェクトの型が実行時の条件(ユーザー入力、設定など)に依存する場合に有用
- オブジェクトの作成に複雑なセットアップが必要な場合に役立つ
自分で試してみよう
-- main.lua
-- PetFactoryを使用してペットを作成する
local PetFactory = require('PetFactory')
-- 入力を読み込む
local petType = io.read()
local petName = io.read()
-- TODO: PetFactory.createPetを使用してペットを作成する
-- TODO: ペットの :speak() メソッドを呼び出し、結果を表示する
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。