ファクトリーパターン
CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 43/57。
ファクトリーパターンは、オブジェクトの生成を1か所にまとめます。呼び出し側は、多くの場合名前で、必要なものを要求し、どのクラスが構築されたかを知らなくても、すぐに使えるオブジェクトを受け取ります。
class Square
def initialize(side)
@side = side
end
def area
@side * @side
end
end
class Circle
def initialize(r)
@r = r
end
def area
(3.14159 * @r * @r).round(2)
end
end
class ShapeFactory
def self.create(kind, size)
case kind
when "square" then Square.new(size)
when "circle" then Circle.new(size)
end
end
end
puts ShapeFactory.create("square", 3).area
puts ShapeFactory.create("hexagon", 3).inspect出力:
9
nil名前からクラスへのハッシュを使うと、ファクトリはより短くなり、拡張も簡単です。クラスはオブジェクトなので、ハッシュに格納して new を送ることができます。
class Dog
def speak
"Woof"
end
end
class Cat
def speak
"Meow"
end
end
class AnimalFactory
KINDS = { "dog" => Dog, "cat" => Cat }.freeze
def self.create(kind)
klass = KINDS.fetch(kind) { raise ArgumentError, "unknown animal: #{kind}" }
klass.new
end
end
puts AnimalFactory.create("cat").speak
begin
AnimalFactory.create("cow")
rescue ArgumentError => e
puts e.message
end出力:
Meow
unknown animal: cowファクトリを使うと、他のコードから実行時に新しい種類を登録することもできます。そのため、種類を追加するためにファクトリを編集する必要はありません。
class Plugins
@kinds = {}
class << self
def register(name, klass)
@kinds[name] = klass
end
def build(name)
@kinds.fetch(name).new
end
def names
@kinds.keys.sort
end
end
end
class Zip
def run
"zipping"
end
end
Plugins.register("zip", Zip)
puts Plugins.build("zip").run
p Plugins.names出力:
zipping
["zip"]呼び出し側が依存するのは、オブジェクトが何をできるか、ここでは area または speak だけであり、具象クラスには決して依存しません。新しい図形を追加するには、新しいクラスを1つとファクトリに新しいエントリを1つ追加するだけで、他の場所を変更する必要はありません:
shape = ShapeFactory.create(kind, size) # Square, Circle, ...
puts shape.area # 呼び出し側はクラス名を指定しないチャレンジ
簡単図形を完成させ、ShapeFactory.create(kind, size)を実装してください。Square、Circle、Triangle(正三角形)は基底クラスShapeを共有します。そのto_sは、クラス名を小文字にし、サイズと面積を小数点以下2桁で表示したsquare 3: area 9.00を返します。円周率には3.14159を使用し、三角形にはMath.sqrt(3) / 4 * size**2を使用してください。ファクトリは、freezeされたハッシュ定数KINDSから種類を検索し、未知の種類に対してはnilを返します。
提供されたコードは各行を読み込みます。たとえばsquare 3を読み取り、各図形をファクトリに要求して出力します。未知の種類の場合はunknown shape: hexagonを出力します。
コードはshape.rb、square.rb、circle.rb、triangle.rb、shape_factory.rbに記述してください。main.rbには提供された入出力コードが含まれており、編集できません。
自分で試してみよう
require_relative 'shape'
require_relative 'square'
require_relative 'circle'
require_relative 'triangle'
require_relative 'shape_factory'
# 提供された入出力コード: そのままにしておく
input = []
while (line = gets)
input << line.chomp
end
input.each do |line|
kind, size = line.split
shape = ShapeFactory.create(kind, size.to_f)
puts shape || "unknown shape: #{kind}"
end
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rubyオンラインコンパイラ