Menu
CoddyTech

Pattern Strategy

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

Il pattern strategy inserisce una parte di un algoritmo in un proprio oggetto e permette all'oggetto principale di delegare a esso. Sostituire la strategia cambia 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 qualsiasi oggetto con cost funziona: 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 sia una lambda, un oggetto metodo o un oggetto di una classe definita da te:

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 comportamento mentre il programma è in esecuzione, oppure quando la variazione riguarda un singolo passaggio. Una sottoclasse fissa il comportamento per tutta la vita 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) è 5 più 1.50 per kg, Express#cost(kg) è 15 più 3 per kg e Pickup#cost(kg) è 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 inizia con Standard e legge i comandi use express, use pickup, use standard o quote 2.5, stampando ogni preventivo.

Il tuo codice va 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