Menu
CoddyTech

Strategiemuster

Teil des Abschnitts Objektorientierte Programmierung der Ruby-Journey von Coddy. Lektion 45 von 57.

Das Strategie-Muster verlagert einen Teil eines Algorithmus in ein eigenes Objekt und lässt das Hauptobjekt an dieses delegieren. Durch den Austausch der Strategie ändert sich das Verhalten, ohne dass die Hauptklasse geändert werden muss:

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

Ausgabe:

8.0
21

Die Strategien haben nur einen Methodennamen gemeinsam, daher funktioniert jedes Objekt mit cost: wieder Duck-Typing. Eine neue Versandart hinzuzufügen bedeutet, eine Klasse hinzuzufügen, nicht einen weiteren Zweig in Shipment:

class Pickup
  def cost(_kg)
    0
  end
end

shipment.strategy = Pickup.new     # Shipment bleibt unverändert

In Ruby ist eine Strategie oft ein Lambda. Alles, worauf call angewendet werden kann, lässt sich speichern und aufrufen, egal ob es sich um ein Lambda, ein Methodenobjekt oder ein Objekt deiner eigenen Klasse handelt:

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)

Ausgabe:

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

Wähle eine Strategie statt Unterklassen, wenn ein Objekt sein Verhalten während der Laufzeit des Programms ändern muss oder wenn die Variation aus einem einzelnen Schritt besteht. Eine Unterklasse legt das Verhalten für die gesamte Lebensdauer des Objekts fest:

# Unterklassenbildung: bei der Erstellung festgelegt
ExpressShipment.new(2)

# Strategie: kann später geändert werden
shipment = Shipment.new(2, Standard.new)
shipment.strategy = Express.new
challenge icon

Aufgabe

Einfach

Vervollständige die Versandstrategien und ShippingCalculator. Standard#cost(kg) beträgt 5 plus 1.50 pro kg, Express#cost(kg) beträgt 15 plus 3 pro kg und Pickup#cost(kg) beträgt 0. Der Rechner wird mit einer Strategie erstellt, kann sie mit strategy= ändern, und quote(kg) gibt 2.5 kg by Express: 22.50 zurück, wobei der Klassenname der Strategie verwendet wird.

Der bereitgestellte Code beginnt mit Standard und liest Befehle wie use express, use pickup, use standard oder quote 2.5 ein und gibt jedes Angebot aus.

Dein Code gehört in standard.rb, express.rb, pickup.rb und shipping_calculator.rb. main.rb enthält den bereitgestellten Ein-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

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

# Vorgegebener Ein-/Ausgabecode: unverändert lassen
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 iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Ruby-Compiler