Menu
Coddy logo textTech

Patrón Strategy

Parte de la sección Programación orientada a objetos del Journey de Ruby de Coddy. Lección 45 de 57.

El patrón strategy coloca una parte de un algoritmo en su propio objeto y permite que el objeto principal delegue en él. Cambiar la estrategia modifica el comportamiento sin cambiar la clase principal:

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

Salida:

8.0
21

Las estrategias solo comparten un nombre de método, así que cualquier objeto con cost funciona: tipado pato de nuevo. Añadir una nueva forma de realizar envíos significa añadir una clase, no otra rama dentro de Shipment:

class Pickup
  def cost(_kg)
    0
  end
end

shipment.strategy = Pickup.new     # Shipment no cambia

En Ruby, una estrategia suele ser una lambda. Cualquier elemento que responda a call puede almacenarse e invocarse, ya sea una lambda, un objeto método o un objeto de tu propia clase:

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)

Salida:

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

Elige la estrategia en lugar de las subclases cuando un objeto deba cambiar su comportamiento mientras el programa se ejecuta, o cuando la variación sea un único paso. Una subclase fija el comportamiento durante toda la vida del objeto:

# subclasificación: fijo en la creación
ExpressShipment.new(2)

# estrategia: se puede cambiar después
shipment = Shipment.new(2, Standard.new)
shipment.strategy = Express.new
challenge icon

Desafío

Fácil

Completa las estrategias de envío y ShippingCalculator. Standard#cost(kg) es 5 más 1.50 por kg, Express#cost(kg) es 15 más 3 por kg y Pickup#cost(kg) es 0. El calculador se crea con una estrategia, puede cambiarla con strategy= y quote(kg) devuelve 2.5 kg by Express: 22.50, utilizando el nombre de la clase de la estrategia.

El código proporcionado comienza con Standard y lee los comandos use express, use pickup, use standard o quote 2.5, mostrando cada cotización.

Tu código debe ir en standard.rb, express.rb, pickup.rb y shipping_calculator.rb. main.rb contiene el código proporcionado de entrada/salida y no se puede editar.

Pruébalo tú mismo

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

# Código de entrada/salida suministrado: déjalo tal como está
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 iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Compilador de Ruby online