ビルダーパターン
CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 48/57。
builderパターンは、複雑なオブジェクトを段階的に作成します。各ステップは1つの選択肢を保存してビルダーを返すメソッドであるため、ステップを連結できます。buildは選択肢を確認し、完成したオブジェクトを返します。
class Pizza
def initialize(size, toppings)
@size = size
@toppings = toppings
end
def to_s
"#{@size} pizza with #{@toppings.join(", ")}"
end
end
class PizzaBuilder
def initialize
@toppings = []
end
def size(value)
@size = value
self
end
def topping(name)
@toppings << name
self
end
def build
raise ArgumentError, "size is required" if @size.nil?
Pizza.new(@size, @toppings)
end
end
puts PizzaBuilder.new.size("large").topping("cheese").topping("ham").build出力:
large pizza with cheese, hamビルダーはbuildでのみオブジェクトを作成するため、必要な部品や、同時に指定できない2つのオプションなど、組み合わせ全体を一度にチェックできます。
class TicketBuilder
def seat(value)
@seat = value
self
end
def standing
@standing = true
self
end
def build
raise ArgumentError, "seat and standing exclude each other" if @seat && @standing
@standing ? "standing ticket" : "ticket for seat #{@seat || "any"}"
end
end
puts TicketBuilder.new.seat("B7").build
puts TicketBuilder.new.standing.build
begin
TicketBuilder.new.seat("A1").standing.build
rescue ArgumentError => e
puts e.message
end出力:
ticket for seat B7
standing ticket
seat and standing exclude each otherRubyでよく使われる形式では、ビルダーをブロックに渡します。クラスメソッドがビルダーを作成し、それをyieldして、最後にbuildを呼び出します:
class Email
attr_accessor :to, :subject, :lines
def self.compose
email = new
email.lines = []
yield email
email
end
def to_s
"To: #{to}\nSubject: #{subject}\n\n#{lines.join("\n")}"
end
end
mail = Email.compose do |e|
e.to = "ada@x.io"
e.subject = "Hi"
e.lines << "Hello Ada,"
e.lines << "see you soon."
end
puts mail出力:
To: ada@x.io
Subject: Hi
Hello Ada,
see you soon.少数のオプション値であれば、デフォルト値を持つ Ruby のキーワード引数で十分なことがよくあります。パーツを繰り返し指定できる場合、順序が重要な場合、またはオブジェクトが存在する前に組み合わせを検証する必要がある場合は、ビルダーを使うメリットがあります:
Pizza.new(size: "large", crust: "thin") # 少ないオプション: キーワード
Query.new.from("users").where("age > 18").where("active = 1").limit(10).to_sチャレンジ
簡単PizzaBuilder と Pizza を完成させてください。ビルダーの手順である size(value)、crust(value)、topping(name)(繰り返し可能)は、ビルダーを返します。サイズが指定されていない場合、build は ArgumentError を size is required とともに発生させ、クラストが選択されていない場合は classic を使用し、Pizza を返します。Pizza#to_s は、large thin pizza with cheese, ham または small classic pizza with no toppings を返します。
各入力行は、size=large crust=thin topping=cheese topping=ham のような1つの注文です。提供されたコードは、各パーツを順番にビルダーへ送り、メソッドチェーンを使って、ピザまたはエラーメッセージを出力します。
コードは pizza.rb と pizza_builder.rb に記述します。main.rb には提供された入出力コードが含まれており、編集できません。
自分で試してみよう
require_relative 'pizza'
require_relative 'pizza_builder'
# 提供された入出力コード: そのままにしておく
input = []
while (line = gets)
input << line.chomp
end
input.each do |line|
builder = line.split.reduce(PizzaBuilder.new) do |b, part|
step, value = part.split("=")
b.public_send(step, value)
end
begin
puts builder.build
rescue ArgumentError => e
puts e.message
end
end
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rubyオンラインコンパイラ