Menu
Coddy logo textTech

Patron Builder

Fait partie de la section Programmation orientée objet du Journey Ruby de Coddy. Leçon 48 sur 57.

Le modèle builder crée un objet complexe étape par étape. Chaque étape est une méthode qui stocke un choix et renvoie le builder, ce qui permet d’enchaîner les étapes ; build vérifie les choix et renvoie l’objet terminé :

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

Sortie :

large pizza with cheese, ham

Comme le builder ne crée l’objet que dans build, il peut vérifier toute la combinaison en une seule fois, par exemple la présence d’une partie obligatoire ou de deux options qui ne peuvent pas être utilisées ensemble :

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

Sortie :

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

Une forme courante en Ruby transmet le builder à un bloc. La méthode de classe crée le builder, lui cède l’exécution, puis appelle build à la fin :

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

Sortie :

To: ada@x.io
Subject: Hi

Hello Ada,
see you soon.

Pour une poignée de valeurs facultatives, les arguments nommés de Ruby avec des valeurs par défaut suffisent souvent. Un générateur devient utile lorsque certaines parties peuvent se répéter, que l’ordre est important ou que la combinaison doit être vérifiée avant que l’objet n’existe :

Pizza.new(size: "large", crust: "thin")          # peu d'options : mots-clés

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

Défi

Facile

Complétez PizzaBuilder et Pizza. Les étapes du générateur size(value), crust(value) et topping(name) (répétable) renvoient le générateur. build lève ArgumentError avec size is required lorsqu'aucune taille n'a été indiquée, utilise la croûte classic lorsqu'aucune n'a été choisie, et renvoie une Pizza. Pizza#to_s renvoie large thin pizza with cheese, ham ou small classic pizza with no toppings.

Chaque ligne d'entrée est une commande telle que size=large crust=thin topping=cheese topping=ham. Le code fourni envoie chaque élément au générateur dans l'ordre, par chaînage, et affiche la pizza ou le message d'erreur.

Votre code doit se trouver dans pizza.rb et pizza_builder.rb. main.rb contient le code d'entrée/sortie fourni et ne peut pas être modifié.

Essayez vous-même

require_relative 'pizza'
require_relative 'pizza_builder'

# Code d'entrée/sortie fourni : le garder tel quel
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 iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Ruby en ligne