Menu
Coddy logo textTech

Wzorzec strategii

Część sekcji Programowanie obiektowe ścieżki Ruby w Coddy. Lekcja 45 z 57.

Wzorzec strategii umieszcza jedną część algorytmu we własnym obiekcie i pozwala głównemu obiektowi delegować do niego zadania. Zmiana strategii modyfikuje zachowanie bez zmiany głównej klasy:

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

Wynik:

8.0
21

Strategie mają wspólną tylko nazwę metody, więc działa każdy obiekt z cost: znów typowanie kacze. Dodanie nowego sposobu wysyłki oznacza dodanie jednej klasy, a nie kolejnej gałęzi wewnątrz Shipment:

class Pickup
  def cost(_kg)
    0
  end
end

shipment.strategy = Pickup.new     # Shipment pozostaje bez zmian

W Ruby strategią jest często lambda. Wszystko, co odpowiada na call, można przechować i wywołać, niezależnie od tego, czy jest to lambda, obiekt metody, czy obiekt własnej klasy:

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)

Wynik:

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

Wybierz strategię zamiast podklas, gdy jeden obiekt musi zmieniać swoje zachowanie podczas działania programu lub gdy zmienność dotyczy jednego kroku. Podklasa ustala zachowanie na cały czas istnienia obiektu:

# dziedziczenie: ustalone przy tworzeniu
ExpressShipment.new(2)

# strategia: można ją później zmienić
shipment = Shipment.new(2, Standard.new)
shipment.strategy = Express.new
challenge icon

Wyzwanie

Łatwy

Uzupełnij strategie wysyłki i ShippingCalculator. Standard#cost(kg) wynosi 5 plus 1.50 za kg, Express#cost(kg) wynosi 15 plus 3 za kg, a Pickup#cost(kg) wynosi 0. Kalkulator jest tworzony ze strategią, można ją zmienić za pomocą strategy=, a quote(kg) zwraca 2.5 kg by Express: 22.50, używając nazwy klasy strategii.

Dostarczony kod rozpoczyna działanie ze strategią Standard i odczytuje polecenia use express, use pickup, use standard lub quote 2.5, wypisując każdą wycenę.

Umieść swój kod w plikach standard.rb, express.rb, pickup.rb i shipping_calculator.rb. Plik main.rb zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.

Spróbuj swoich sił

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

# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Ruby online