Menu
CoddyTech

ファクトリーパターン

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                           # 呼び出し元はクラス名を指定しない
challenge icon

チャレンジ

簡単

図形とShapeFactory.create(kind, size)を完成させてください。Square、Circle、Triangle(正三角形)は基底クラスShapeを共有します。そのto_sは、クラス名を小文字にしたもの、サイズ、面積を小数点以下2桁で含むsquare 3: area 9.00を返します。円周率には3.14159を使用し、三角形にはMath.sqrt(3) / 4 * size**2を使用してください。ファクトリは、凍結されたハッシュ定数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
quiz icon腕試し

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

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

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