Menu
Coddy logo textTech

Pattern Factory

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

Il pattern factory concentra la creazione degli oggetti in un unico posto. Chi chiama richiede ciò di cui ha bisogno, spesso indicandolo per nome, e riceve un oggetto già pronto senza sapere quale classe è stata istanziata:

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 facile da estendere. Le classi sono oggetti, quindi possono essere memorizzate in una mappa e inviare loro 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 indica 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 greco 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'

# Supplied input/output code: keep it as it is
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