Menu
Coddy logo textTech

ファクトリメソッド

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

ファクトリメソッドは、インスタンスを作成して返すクラスメソッドです。オブジェクトを構築する方法の1つに名前を付けるものです。クラスメソッド内では、newはself.newを意味します:

class Temperature
  attr_reader :celsius

  def initialize(celsius)
    @celsius = celsius
  end

  def self.from_fahrenheit(f)
    new((f - 32) * 5 / 9.0)
  end

  def self.freezing
    new(0)
  end
end

puts Temperature.from_fahrenheit(212).celsius
puts Temperature.freezing.celsius

出力:

100.0
0

テキストをオブジェクトに変換することは、ファクトリの一般的な役割です。パース処理をクラスの近くに置き、initialize はシンプルなままにします。

class Duration
  attr_reader :seconds

  def initialize(seconds)
    @seconds = seconds
  end

  def self.parse(text)
    new(text.split(":").map(&:to_i).reduce(0) { |acc, part| acc * 60 + part })
  end
end

puts Duration.parse("1:30").seconds
puts Duration.parse("2:00:05").seconds

出力:

90
7205

ファクトリは不正な入力を拒否できます。nilを返すと、呼び出し元はifで結果をテストできます。エラーを発生させると、呼び出し元がそれを救出しない限りプログラムは停止します。

class Color
  attr_reader :hex

  def initialize(hex)
    @hex = hex
  end

  def self.parse(text)
    return nil unless text.match?(/\A#[0-9a-f]{6}\z/i)

    new(text.downcase)
  end
end

["#FF8800", "orange"].each do |text|
  color = Color.parse(text)
  puts color ? "ok #{color.hex}" : "not a color: #{text}"
end

出力:

ok #ff8800
not a color: orange

private_class_method :new によって new はクラス内部からのみ呼び出し可能になるため、オブジェクトを作成する唯一の方法がファクトリになります。

class Money
  attr_reader :cents

  def initialize(cents)
    @cents = cents
  end
  private_class_method :new

  def self.from_cents(cents)
    new(cents)
  end
end

puts Money.from_cents(250).cents
begin
  Money.new(250)
rescue NoMethodError => e
  puts e.message
end

出力:

250
private method `new' called for class Money
challenge icon

チャレンジ

簡単

Durationクラスを完成させてください。initialize(seconds)は秒数を格納します。ファクトリDuration.parse(text)はm:ssまたはh:mm:ssを読み取り、ファクトリDuration.from_minutes(m)は分単位の整数を受け取り、plus(other)は合計値を持つ新しいDurationを返し、to_sは分と秒を2桁で表示したh:mm:ssを返します。

提供されたコードは時間を読み取ります。45mのような行はfrom_minutesを通り、それ以外はparseを通ります。各時間を出力し、最後にtotal 1:47:10を出力します。

コードはduration.rbに記述してください。main.rbには提供された入出力コードが含まれており、編集できません。

自分で試してみよう

require_relative 'duration'

# 提供された入出力コード: そのままにしておく
input = []
while (line = gets)
  input << line.chomp
end
durations = input.map do |line|
  line.end_with?("m") ? Duration.from_minutes(line.to_i) : Duration.parse(line)
end
durations.each { |d| puts d }
puts "total #{durations.reduce(Duration.new(0)) { |sum, d| sum.plus(d) }}"
quiz icon腕試し

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

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

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