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.