Menu
Coddy logo textTech

Pattern Strategy

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

Il pattern strategy mette una parte dell’algoritmo in un proprio oggetto e consente all’oggetto principale di delegare a esso. Sostituire la strategia modifica il comportamento senza modificare la classe principale:

class Standard
  def cost(kg)
    5 + kg * 1.5
  end
end

class Express
  def cost(kg)
    15 + kg * 3
  end
end

class Shipment
  attr_writer :strategy

  def initialize(kg, strategy)
    @kg = kg
    @strategy = strategy
  end

  def cost
    @strategy.cost(@kg)
  end
end

s = Shipment.new(2, Standard.new)
puts s.cost
s.strategy = Express.new
puts s.cost

Output:

8.0
21

Le strategie condividono solo il nome di un metodo, quindi funziona qualsiasi oggetto con cost: ancora una volta, duck typing. Aggiungere un nuovo modo di spedire significa aggiungere una classe, non un altro ramo all'interno di Shipment:

class Pickup
  def cost(_kg)
    0
  end
end

shipment.strategy = Pickup.new     # Shipment rimane invariato

In Ruby una strategia è spesso una lambda. Qualsiasi cosa risponda a call può essere memorizzata e invocata, che si tratti di una lambda, di un oggetto metodo o di un oggetto di una tua classe:

class TextFormatter
  def initialize(strategy)
    @strategy = strategy
  end

  def format_all(words)
    words.map { |w| @strategy.call(w) }.join(" ")
  end
end

class Censor
  def call(word)
    word.size > 3 ? "*" * word.size : word
  end
end

words = %w[ruby is great]
puts TextFormatter.new(->(w) { w.upcase }).format_all(words)
puts TextFormatter.new(:capitalize.to_proc).format_all(words)
puts TextFormatter.new(Censor.new).format_all(words)

Output:

RUBY IS GREAT
Ruby Is Great
**** is *****

Scegli la strategia invece delle sottoclassi quando un oggetto deve cambiare il proprio comportamento mentre il programma è in esecuzione, oppure quando la variazione riguarda un singolo passaggio. Una sottoclasse fissa il comportamento per l'intera durata dell'oggetto:

# creazione di sottoclassi: fissata alla creazione
ExpressShipment.new(2)

# strategia: può cambiare in seguito
shipment = Shipment.new(2, Standard.new)
shipment.strategy = Express.new
challenge icon

Sfida

Facile

Completa le strategie di spedizione e ShippingCalculator. Standard#cost(kg) è pari a 5 più 1.50 per kg, Express#cost(kg) è pari a 15 più 3 per kg e Pickup#cost(kg) è pari a 0. Il calcolatore viene creato con una strategia, può cambiarla con strategy= e quote(kg) restituisce 2.5 kg by Express: 22.50, usando il nome della classe della strategia.

Il codice fornito parte da Standard e legge i comandi use express, use pickup, use standard oppure quote 2.5, stampando ogni preventivo.

Il tuo codice va inserito in standard.rb, express.rb, pickup.rb e shipping_calculator.rb. main.rb contiene il codice di input/output fornito e non può essere modificato.

Provalo tu

require_relative 'standard'
require_relative 'express'
require_relative 'pickup'
require_relative 'shipping_calculator'

# Codice di input/output fornito: lascialo così com'è
input = []
while (line = gets)
  input << line.chomp
end
strategies = { "standard" => Standard, "express" => Express, "pickup" => Pickup }
calc = ShippingCalculator.new(Standard.new)
input.each do |line|
  cmd, arg = line.split
  if cmd == "use"
    calc.strategy = strategies[arg].new
  else
    puts calc.quote(arg.to_f)
  end
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