Menu
Coddy logo textTech

Strategiemuster

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

Das Strategie-Muster legt einen Teil eines Algorithmus in ein eigenes Objekt und ermöglicht es dem Hauptobjekt, diesen Teil zu delegieren. Das Austauschen der Strategie ändert das Verhalten, ohne die Hauptklasse zu ändern:

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 teilen sich nur einen Methodennamen, daher funktioniert jedes Objekt mit cost: wieder Duck-Typing. Eine neue Versandmöglichkeit hinzuzufügen bedeutet, eine Klasse hinzuzufügen, nicht einen weiteren Zweig innerhalb von Shipment:

class Pickup
  def cost(_kg)
    0
  end
end

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

In Ruby ist eine Strategie häufig eine Lambda-Funktion. Alles, was auf call reagiert, kann gespeichert und aufgerufen werden, unabhängig davon, ob es sich um eine Lambda-Funktion, 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 Strategie statt Unterklassen, wenn ein Objekt sein Verhalten während der Programmausführung ändern muss oder wenn die Variation in einem einzelnen Schritt besteht. Eine Unterklasse legt das Verhalten für die gesamte Lebensdauer des Objekts fest:

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

# Strategy: 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 mit strategy= geändert werden, und quote(kg) gibt 2.5 kg by Express: 22.50 unter Verwendung des Klassennamens der Strategie zurück.

Der bereitgestellte Code beginnt mit Standard und liest die Befehle 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 Eingabe-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

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

# Bereitgestellter Ein-/Ausgabe-Code: belasse ihn wie er ist
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