Strategie

Problem

Eine Familie von Algorithmen soll gekapselt werden, mit der Möglichkeit, sie beliebig auszutauschen.

Motivation

Das Strategie-Muster bietet sich immer dann an, wenn eine Aufgabe mit unterschiedlichen Verfahren, die sich beispielsweise in Geschwindigkeit und Speicherverbrauch unterscheiden, zu lösen ist. Der Eingabe entsprechend kann so das jeweils beste Verfahren dynamisch zur Laufzeit verwendet werden. Es kann ebenfalls genutzt werden, wenn verwandte Klassen sich nur in ihrem Verhalten unterscheiden oder eine Klasse unterschiedliche Verhaltensweisen definiert und diese mit unübersichtlichen if-then-else- oder switch-case-Konstruktionen in ihren Methoden implementiert sind. Zusammenhängende Zweige dieser Bedingungsanweisungen können übersichtlich in eigene Strategieklassen ausgelagert werden.

Lösung

Die Verwandtschaft der Klassen wird durch Implementieren einer gemeinsamen Schnittstelle zum Ausdruck gebracht. Die Verwenderklasse arbeitet mit einem Objekt einer Klasse, die diese Schnittstelle bereitstellt. Dieses Objekt kann zur Laufzeit durch eine Methode setStrategy($strategy) ausgetauscht werden, wodurch das Verhalten der Verwenderklasse dynamisch geändert werden kann.

Anwendungsbeispiele

Beispiel 7.13 zeigt eine Implementierung des bekannten Sortierverfahrens Bubble-Sort. In der hier gezeigten Variante lässt sich der Vergleich von zwei Elementen der zu sortierenden Menge durch Verwendung einer Strategie austauschen.

Beispiel 7.10: Die Schnittstelle CompareStrategy

<?php
interface CompareStrategy {
    public function compare($a, $b);
}
?>


Beispiel 7.11: Die Klasse AscendingCompare

<?php
require_once 'CompareStrategy.php';
 
class AscendingCompare implements CompareStrategy {
  public function compare($a, $b) {
    return ($a == $b) ? 0 : ($a > $b) ? 1 : -1;
  }
}
?>


Beispiel 7.12: Die Klasse DescendingCompare

<?php
require_once 'CompareStrategy.php';
 
class DescendingCompare implements CompareStrategy {
  public function compare($a, $b) {
    return ($a == $b) ? 0 : ($a < $b) ? 1 : -1;
  }
}
?>


Beispiel 7.13: Die Klasse BubbleSort

<?php
require_once 'CompareStrategy.php';
require_once 'AscendingCompare.php';
require_once 'DescendingCompare.php';
 
class BubbleSort {
  private $strategy;
 
  public function setStrategy(CompareStrategy $strategy) {
    $this->strategy = $strategy;
  }
 
  public function sort($array) {
    for ($i = sizeof($array)-1; $i >= 0; --$i) {
      for ($j = 0; $j < $i; ++$j ) {
        $cmp = $this->strategy->compare(
          $array[$j],
          $array[$j+1]
        );
 
        if ($cmp > 0) {
          $tmp         = $array[$j];
          $array[$j]   = $array[$j+1];
          $array[$j+1] = $tmp;
        }
      }
    }
 
    return $array;
  }
}
 
$bs = new BubbleSort;
 
$bs->setStrategy(new AscendingCompare);
print_r($bs->sort(array(22, 4, 1978)));
 
$bs->setStrategy(new DescendingCompare);
print_r($bs->sort(array(22, 4, 1978)));
?>
Array
(
    [0] => 4
    [1] => 22
    [2] => 1978
)

Array
(
    [0] => 1978
    [1] => 22
    [2] => 4
)


In seiner Zielrichtung ist das Strategie-Muster mit der Schablonenmethode verwandt. Der Unterschied zwischen diesen beiden Mustern liegt in der Wahl des Mittels, mit dem man das Ziel zu erreichen versucht: Während das Strategie-Muster mittels Delegation den gesamten Algorithmus zur Laufzeit austauschbar macht, nutzt das Muster der Schablonenmethode Vererbung, um einzelne Schritte einer Operation variabel zu gestalten.