Le design pattern Strategy fait partie de la famille des design patterns comportementaux; il facilite l’utilisation d’algorithmes interchangeables à l’exécution, c’est à dire dynamiquement. Il obéit au bon vieux principe de la programmation orientée objet : Encapsuler ce qui varie.
Les algorithmes à encapsuler
Pour un comportement identique (reagir) nous avons trois implémentations différentes. Ce sont elles que nous souhaitons isoler pour réduire au maximum l’impact du changement qui tôt ou tard se produira dans notre code. Ce comportement commun attend en paramètre un objet se conformant à l’interface StrategieInterface et ses implémentations variables consistent à ici à appliquer un traitement rudimentaire à la méthode donnerPhrase de cet objet en paramètre.
interface StrategieInterface { public function reagir(PersonneInterface $personne): string; } class Enerve implements StrategieInterface { public function reagir(PersonneInterface $personne): string { return strtoupper($personne->donnerPhrase().' !!!').PHP_EOL; } } class Geek implements StrategieInterface { public function reagir(PersonneInterface $personne): string { return str_replace('o', '0', $personne->donnerPhrase()).PHP_EOL; } } class Jovial implements StrategieInterface { public function reagir(PersonneInterface $personne): string { return ucfirst($personne->donnerPhrase()).' :)'.PHP_EOL; } }
Voici notre classe Personne et l’interface qu’elle implémente. La seule chose qu’elle fait est de retourner la chaîne de caractères bonjour !
interface PersonneInterface { public function donnerPhrase(): string; } class Personne implements PersonneInterface { public function donnerPhrase(): string { return 'bonjour'; } }
L’objet Contexte
Voici enfin notre objet Contexte, partie intégrante de notre pattern. Il gère simplement une référence à la stratégie, à laquelle il transmet les requêtes des clients via la délégation à la méthode reagir. C’est en quelque sorte le liant entre le code client et nos différentes stratégies concrètes.
class Contexte { private $strategie; public function __construct(StrategieInterface $strategie) { $this->strategie = $strategie; } public function exprimeReaction(PersonneInterface $personne): string { return $this->strategie->reagir($personne); } }
Pour finir, écrivons le code client qui va utiliser notre design pattern. Evidemment, nous ne nous adressons pas directement aux stratégies mais nous passons par l’objet Contexte, que nous initialiserons successivement avec des instances de chacune des stratégies concrètes.
$personne = new Personne(); $humeurs = [new Enerve(), new Geek(), new Jovial()]; foreach ($humeurs as $humeur) { $contexte = new Contexte($humeur); echo $contexte->exprimeReaction($personne); }
A garder en tête:
- Un trop grand nombre de if (syndrome appelé la forêt d’ifs) est souvent le signe que Strategy doit être envisagé
- Gare à la multiplication intempestive des stratégies concrètes, leur nombre peut vite augmenter !
- Toutes les stratégies concrètes implémentent la même abstraction, il peut parfois arriver qu’elles reçoivent des informations qui ne leur seront pas utiles
Merci pour le writeup, je voulais savoir la différence entre Strategy et Factory, et à quel moment on pourrait servir des 2 à la fois ! merci