Menu
CoddyTech

Pattern Factory

Fa parte della sezione Programmazione orientata agli oggetti del percorso Ruby di Coddy. Lezione 43 di 57.

Il pattern factory centralizza la creazione degli oggetti. Chi chiama richiede ciò di cui ha bisogno, spesso indicandolo per nome, e riceve un oggetto pronto senza sapere quale classe è stata creata:

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

Output:

9
nil

Una mappa dai nomi alle classi rende la factory più breve e più facile da estendere. Le classi sono oggetti, quindi possono essere memorizzate in una mappa e inviate a 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

Output:

Meow
unknown animal: cow

Una factory può anche consentire ad altro codice di registrare nuovi tipi in fase di esecuzione, così aggiungere un tipo non significa modificare la factory:

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

Output:

zipping
["zip"]

I chiamanti dipendono solo da ciò che gli oggetti possono fare, qui area o speak, mai dalle classi concrete. Una nuova forma significa una nuova classe e una nuova voce nella factory, senza modifiche in nessun altro punto:

shape = ShapeFactory.create(kind, size)   # Square, Circle, ...
puts shape.area                           # il chiamante non nomina mai una classe
challenge icon

Sfida

Facile

Completa le forme e ShapeFactory.create(kind, size). Square, Circle e Triangle (equilatero) condividono la classe base Shape, il cui to_s restituisce square 3: area 9.00, con il nome della classe in minuscolo, la dimensione e l'area con due cifre decimali. Usa 3.14159 per pi e Math.sqrt(3) / 4 * size**2 per il triangolo. La factory cerca il tipo in una costante hash congelata KINDS e restituisce nil per un tipo sconosciuto.

Il codice fornito legge righe come square 3, chiede alla factory di creare ogni forma e la stampa, oppure stampa unknown shape: hexagon.

Il tuo codice va inserito in shape.rb, square.rb, circle.rb, triangle.rb e shape_factory.rb. main.rb contiene il codice di input/output fornito e non può essere modificato.

Provalo tu

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

# Codice di input/output fornito: lascialo così com'è
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 iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Ruby online