Menu
CoddyTech

Patron Factory

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

Le pattern factory centralise la création des objets. 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é créée :

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

Une table de hachage 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 une table de hachage 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

Résultat :

Meow
unknown animal: cow

Une fabrique peut aussi permettre à d’autres codes d’enregistrer de nouveaux types à l’exécution. Ainsi, ajouter 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 implique une nouvelle classe et une nouvelle entrée dans la fabrique, sans aucun changement ailleurs :

shape = ShapeFactory.create(kind, size)   # Square, Circle, ...
puts shape.area                           # l’appelant ne nomme jamais de 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 des lignes square 3, demande à la fabrique de créer chaque forme et l’affiche, ou affiche unknown shape: hexagon.

Ton code va dans shape.rb, square.rb, circle.rb, triangle.rb et shape_factory.rb. main.rb contient le code d’entrée/sortie fourni 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 : conservez-le 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