Menu
Coddy logo textTech

Patron Factory

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

Le modèle de fabrique regroupe la création des objets en un seul endroit. Les appelants demandent ce dont ils ont besoin, souvent par nom, et reçoivent un objet prêt à l’emploi sans savoir quelle classe a été construite :

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

Résultat :

9
nil

Un hash associant des noms à des classes rend la fabrique plus courte et plus facile à étendre. Les classes sont des objets, elles peuvent donc être stockées dans un hash et recevoir 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

Sortie :

Meow
unknown animal: cow

Une fabrique peut également permettre à d’autres morceaux de code d’enregistrer de nouveaux types à l’exécution, de sorte que l’ajout d’un type ne nécessite pas de modifier la fabrique :

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

Résultat :

zipping
["zip"]

Les appelants dépendent uniquement de ce que les objets peuvent faire, ici area ou speak, jamais des classes concrètes. Une nouvelle forme signifie une nouvelle classe et une nouvelle entrée dans la fabrique, sans aucune modification ailleurs :

shape = ShapeFactory.create(kind, size)   # Square, Circle, ...
puts shape.area                           # l'appelant ne nomme jamais une classe
challenge icon

Défi

Facile

Complète les formes et ShapeFactory.create(kind, size). Square, Circle et Triangle (équilatéral) partagent la classe de base Shape, dont to_s renvoie square 3: area 9.00, avec le nom de la classe en minuscules, la taille et l’aire avec deux décimales. Utilise 3.14159 pour pi et Math.sqrt(3) / 4 * size**2 pour le triangle. La fabrique recherche le type dans une constante de hachage gelée KINDS et renvoie nil pour un type inconnu.

Le code fourni lit les lignes square 3, demande à la fabrique de créer chaque forme et l’affiche, ou affiche unknown shape: hexagon.

Ton code doit se trouver dans shape.rb, square.rb, circle.rb, triangle.rb et shape_factory.rb. main.rb contient le code fourni d’entrée/sortie et ne peut pas être modifié.

Essayez vous-même

require_relative 'shape'
require_relative 'square'
require_relative 'circle'
require_relative 'triangle'
require_relative 'shape_factory'

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