Menu
Coddy logo textTech

Builder-Muster

Teil des Abschnitts Objektorientierte Programmierung der Ruby-Journey von Coddy. Lektion 48 von 57.

Das Builder-Muster erstellt ein komplexes Objekt Schritt für Schritt. Jeder Schritt ist eine Methode, die eine Auswahl speichert und den Builder zurückgibt, sodass die Schritte verkettet werden können; build überprüft die Auswahlen und gibt das fertige Objekt zurück:

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

Ausgabe:

large pizza with cheese, ham

Da der Builder das Objekt erst in build erstellt, kann er die gesamte Kombination auf einmal überprüfen, etwa auf ein erforderliches Teil oder zwei Optionen, die nicht zusammen verwendet werden können:

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

Ausgabe:

ticket for seat B7
standing ticket
seat and standing exclude each other

Eine übliche Ruby-Form übergibt den Builder an einen Block. Die Klassenmethode erstellt den Builder, übergibt ihn und ruft am Ende build auf:

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

Ausgabe:

To: ada@x.io
Subject: Hi

Hello Ada,
see you soon.

Für eine Handvoll optionaler Werte reichen Rubys Schlüsselwortargumente mit Standardwerten oft aus. Ein Builder lohnt sich, wenn Teile wiederholt vorkommen können, die Reihenfolge wichtig ist oder die Kombination geprüft werden muss, bevor das Objekt erstellt wird:

Pizza.new(size: "large", crust: "thin")          # wenige Optionen: Schlüsselwörter

Query.new.from("users").where("age > 18").where("active = 1").limit(10).to_s
challenge icon

Aufgabe

Einfach

Vervollständige PizzaBuilder und Pizza. Die Schritte des Builders size(value), crust(value) und topping(name) (wiederholbar) geben den Builder zurück. build löst ArgumentError mit size is required aus, wenn keine Größe angegeben wurde, verwendet classic als Teig, wenn keiner ausgewählt wurde, und gibt eine Pizza zurück. Pizza#to_s gibt large thin pizza with cheese, ham oder small classic pizza with no toppings zurück.

Jede Eingabezeile ist eine Bestellung wie size=large crust=thin topping=cheese topping=ham. Der bereitgestellte Code sendet jeden Teil der Reihe nach durch Verkettung an den Builder und gibt die Pizza oder die Fehlermeldung aus.

Dein Code gehört in pizza.rb und pizza_builder.rb. main.rb enthält den bereitgestellten Ein-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

require_relative 'pizza'
require_relative 'pizza_builder'

# Mitgelieferter Ein-/Ausgabecode: belasse ihn wie er ist
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
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Ruby-Compiler