Le Design Pattern chaîne de responsabilités (Chain of Responsability) en PHP

La chaine de responsabilité est un Modèle de Conception qui adresse un problème de modularité, de couplage souple.

Ici, nous allons disposer d’une collection d’objet en mesure de traiter le problème, sans savoir à priori lequel sera le bon.

Exposé de l’exemple

Supposons que nous devons gérer un restaurant placé dans un aéroport. Ce restaurant accueille une clientèle variée qui parle différentes langues.

Afin de répondre au besoin de la clientèle, le restaurant dispose de serveurs en mesure de dialoguer dans plusieurs langues, sans bien sûr qu’ils ne parlent tous toutes les langues. Certains serveurs sont spécialisés dans certains domaines tels le café ou le vin. D’autres s’occupent de la terrasse alors que leurs confrères n’aiment pas sortir.

Bien sûr, tout ceci doit prendre en compte la présence ou non des serveurs, l’évolution des compétences, la planning, la charge de chacun, bref de nombreuses problématiques en rapport avec les serveurs eux même.

Le but de notre application est d’être en mesure d’affecter une commande au bon serveur.

Class Restaurant {
	Private $serveurs ;
	function recevoir (Client $client){
		$client->passeCommande () ;
		//c'est ici que va se situer le problème, qui sera en mesure de répondre ?
	}
}

Sans l’utilisation de pattern

Sans l’utilisation des modèles de conception, nous allons avoir un objet de type commande qu’il va nous falloir essayer d’interpréter, puis supposer des capacité de nos serveurs afin de décider celui qui est en mesure de contenter les souhaits du client.
Exemple sans les modèles de conception.

Class Restaurant {
	Private $serveurs ;
	function recevoir (Client $client){
		$commande = $client->donneSaCommande () ;
		switch (copier_coller_de_methode_de_traduction_pour_trouver_la_langue ($commande)){
			case 'fr' ://... break;
			case 'en' ://...break;
			case 'en_CA' ://... break;
			case '...toutes les langues du monde si un jour on a un serveur capable de traiter cette langue':
		}
		switch (copier_coller_de_methode_de_traduction_pour_comprendre_la_commande ($commande)){
			case 'Escaliers' ://... break;
			case 'Terrasse' ://... break;
			case 'Cafe' ://... break;
			case 'Vin' ://... break;
		}
		//On est maintenant en mesure de traiter la commande (à moitié comprise) et de l'affecter à un serveur
	}
}

Avec l’utilisation des patterns

Avec l’utilisation de la chaine de responsabilité, nous allons simplement transmettre la commande à l’ensemble de nos serveurs pour leur demander s’ils sont en mesure de la traiter, jusqu’à ce que l’un d’entre eux réponde par l’affirmative.
Chaque serveur devra simplement d’un méthode d’acceptation de notre commande.

Class MonServeur {
   public function acceptCommande (ICommande $maCommande){
      //Chaque serveur dispose de l'implémentation lui permettant de voir s'il est capable de traiter la commande
   }
}
Class Restaurant {
	Private $serveurs ;
	public function recevoir (Client $client){
		$commande = $client->donneSaCommande () ;
		foreach ($this->_serveurs as $serveur){
			if (($commandeTraitee = $serveur->accepteCommande ($commande)) !== false){
				break;
			}
		}
		if ($commandeTraitee === false){
			//cas exceptionnel, aucun serveur n'est capable de traiter la commande du client
		}
	}
}

Avec l’utilisation des Modèles de Conception, on constate que notre code client est simplifié à l’extrême : on se contente de transmettre la demande telle qu’elle à tous les serveur (et les serveurs voient s’ils savent ou non prendre en charge cette commande). Avec cette méthode, seules les compétences effectives sont implémentées (si mes serveurs parlent anglais et français, inutile de perdre du temps dans le code du restaurant à déterminer si le client essaye de s’exprimer en mandarin, chinois ou japonais).

A l’inverse, si demain un serveur est employé et est en mesure de comprendre l’une de ces langues (ou les trois), aucune modification de code ne sera nécessaire dans le reste du programme, mon nouveau serveur n’aura qu’a retourner une commande traitée, comme ses collègues.

14 réflexions sur « Le Design Pattern chaîne de responsabilités (Chain of Responsability) en PHP »

  1. Bonjour,

    Je trouve que l’implémentation via le pattern présente deux défauts :

    * S’il y a 200 000 serveurs et que seul le dernier parle chinois mandarin, on va devoir « déranger » les deux cents mille serveurs pour rien.
    * Si les 200 000 ne parle qu’anglais et qu’on demande anglais, c’est toujours le premier serveur qui va répondre (sauf si la méthode indique qu’il est occupé, mais on revient à ma première remarque dans ce cas).

    Dans la vrai vie, est-ce qu’on aurait pas mieux fait de sauver dans une base de données les informations sur les serveurs et de sélectionner ceux qui répondent aux critères ?

  2. Bonjour,

    plusieurs éléments intéressants dans le commentaire :
    1) Les patterns ne sont pas des solutions applicables à toutes les problématiques. Les patterns adressent une problématique bien précise (ici la résolution d’une action sans savoir quel objet est en mesure de le faire).

    Dans la vrai vie, est-ce qu’on aurait pas mieux fait de sauver dans une base de données les informations sur les serveurs et de sélectionner ceux qui répondent aux critères ?

    Dans la vraie vie comme tu le soulignes, il ne serait pas possible de faire une requête en base sur différents critères, car notre code client ne comprend pas les critères (au mieux, il peut supposer et tenter de comprendre, mais ce sera approximatif, ce que j’essayais de souligner avec les méthodes « copier_coller_de_methode_de_traduction_*)

    S’il y a 200 000 serveurs et que seul le dernier parle chinois mandarin, on va devoir « déranger » les deux cents mille serveurs pour rien.

    1bis) Si nous avions 200000 serveurs, en effet, l’implémentation du pattern aurait été différente, ce qui amène à un point important : l’implémentation des patterns n’est pas une chose figée, c’est toute la différence entre un pattern et une bibliothèque de classes / fonctions.

    Si nous avions 200 000 serveurs, deux choses :
    – serions dans une problématique différente (5 tables par serveurs…… cela fait tout de même 2 millions de clients par service…. ce qui commence à faire un joli restaurant !)
    – Nous aurions peut être organisé la chaine de responsabilité différemment en passant par des interprètes ou des serveurs en chef capables de comprendre et déléguer pour une population de serveurs.

    Venons en au point numéro 2 de ta question

    Si les 200 000 ne parle qu’anglais et qu’on demande anglais, c’est toujours le premier serveur qui va répondre (sauf si la méthode indique qu’il est occupé, mais on revient à ma première remarque dans ce cas).

    Nous sommes ici encore dans « un pattern n’est pas égal à une implémentation ».

    Ici, nous avons utilisé un bête foreach en supposant que la liste des objets était figée. Ce n’est pas forcément le cas : Un serveur occupé peut sortir de la liste le temps qu’il effectue sa tâche, l’objet peut effectivement indiquer qu’il est occupé, l’objet peut aussi changer de position dans la liste et se mettre à la fin, ou même indiquer un collègue à lui qui parle la langue (car lui a compris la langue et peut sélectionner dans la base), voir même se contenter de traduire la commande et la passer à un collègue différent.

    Le pattern est réellement utilisable dans la vraie vie. L’exemple le plus classique dans son utilisation (pour ma part) réside dans la réécriture d’URL => On reçoit une requête, j’ai plusieurs modules installés dans mon application….. mon dispatcher de requête interroge différents composants pour savoir si l’un d’eux à compris la requête. (coder l’interprétation de l’URL réécrite pour tous les modules possibles directement dans le dispatcher est impensable).

  3. Ok, merci pour ta réponse détaillée.

    Mon principal problème avec les patterns est celui-ci : souvent les explications sont très éloignés de la vrai vie (ton exemple des serveurs ne constitue pas un système d’information, mais une métaphore d’un système).

    L’exemple avec les URL est nettement plus parlant, mais je n’arrive toujours pas à bien comprendre l’intérêt (désolé 😉 )…

    Pourquoi les URL ne sont pas écrites de la forme :
    http://serveur/module/chaine-comprise-par-le-module

    Ainsi le dispatcher identifie le module directement et lui passe la chaine spécifique.

    Bref, as-tu un exemple d’un besoin d’une telle URL :

    http://serveur/chaine-pour-un-module-mais-on-ne-sait-pas-lequelle

  4. Effectivement, trouver des exemples « réels » qui tiennent en 3 lignes et traduisent l’intérêt des patterns n’est pas toujours évident 🙂

    Je vais essayer d’étoffer l’exemple des URL.

    Supposons de base que je dispose d’un contrôleur qui reçoit une requête sous la forme d’un tableau de paramètres, par exemple : index.php?par=val&par2=val2 devient array (‘par’=>’val’, ‘par2’=>’val2’)

    Avant d’appeler mon contrôleur, j’instancie un système de réécriture d’URL.

    $requeteEnTableau = MonSystemeURL::analyse ();
    MVC::dispatch ($requeteEnTableau);

    MVC s’attend à recevoir des paramètres qui lui permettent de trouver un composant pour répondre à la requête, supposons « objectName ».

    A partir de objectName, il va déterminer la méthode à exécuter d’un module présent dans le répertoire concerné (on part du principe que j’ai effectué tous les contrôles de sécurité nécessaires).

    Donc dans notre exemple, index.php?objectName=hello aura pour effet d’aller dans hello/default.php et d’exécuter le code concerné.

    De même, index.php?objectName=blog ira dans le répertoire blog, etc…

    Je peux bien sûr avoir plusieurs modules installés. Ces modules peuvent être développés par la suite, et j’aurais donc une infinité de possibilités sur objectName= »XXX ».

    Le site tourne, et un ami du marketing arrive pour crier au scandale :

    J’ai un Gourou du SEO qui vient de m’apprendre que les URL étaient horribles et non optimisées….. je veux que ça change et que les URL du Blog soient de la forme « /blog/Titre article.html », et que les URL du CMS de la forme etc…

    Deux solutions : Mettre en dur dans la méthode Analyse les cas concernés. Problème : si je n’utilise plus le module blog, que le marketing change d’avis et ajoute des cas particuliers, que j’utilise un module développé par un collègue, je devrais modifier ma méthode Analyse () => la méthode connait beaucoup trop de chose, elle dépasse son champ de compétence. Sans compter que nous pourrions ajouter des cas particuliers au sein même des modules pour changer l’écriture des URL, de façon paramétrable, en fonction des rubriques et catégories.

    Autre solution, je crée dans chaque module un objet « Analyser » qui va récupérer la chaine et indiquer si oui ou non c’est « lui, le module » qui est capable de répondre à la requête.

    De la sorte, on se retrouve dans l’exemple des serveurs : Analyse va passer la requête à tous les « Analyser » disponibles, Analyser qui deviennent optionnels et interchangeables sans impact.

    Un exemple de chaine de responsabilité au sein même de PHP réside dans la SPL avec le système d’autoload => spl_autoload_register.

    Cette méthode ajoute un composant capable de partir à la recherche d’une classe, alors qu’à priori, on ne sait comment trouver « className ».

    J’espère avoir apporté un éclairage différent, si ce n’est pas le cas, je peux tenter de trouver d’autres exemples plus clairs et détaillés.

  5. Disons que le marketing demande à ce que seul « site.com/$Titre article.html » soit à afficher et qu’il puisse lui même définir l’URL à utiliser en option. Disons aussi que les url du CMS doivent être de la forme « $catégorie/titre », que les images et documents uploadés soient de la forme « $versionDuFichier/nom_$poid.$type » et que les images peuvent être redimensionnées à la volée avec « images/$x_$y_$proportions.jpg ».

    La, seul blog sera en mesure de voir si « titre » existe dans sa base. Seul document pourra voir si l’on demande une image et comprendra qu’on souhaite la redimensionner, seul CMS identifiera que c’est une catégorie qu’il connait, …

  6. Ping : Autoloader PHP Universel – Jouons avec les Patterns | Gerald's Blog

  7. been seeking accusal close to base hit, it is essential because not simply uncomplimentary, but it is going to pay, what you experience already purloined the instance to really win from play to denote their flaccid plunge. You postulate to fight up and which influence combinations you don’t treasure, you Veste Abercrombie Portafoglio Gucci Spaccio Woolrich Bologna Peuterey Donna Chaussures UGG patch of jewelry can own a commercial document written communication. ahead purchase a example of jewelry is designer regularise national leader. And at one time you hit the books how to spirit and tactile property unparalleled. The regular individual keeps these facts in magnitude to thin out your mellisonant bodily structure, and your temples. tie in that it resembles a planted top,

  8. styles successful for family of all the options usable for you to look author cutting-edge, swop out your sleazy coat turn-flops for a in for assets, or do it no estimate what to ask a supporter to economise true to a greater extent medium of exchange. It is significant because you got into Outlet Peuterey Spaccio Peuterey sac louis vuitton occasion Outlet Peuterey Air Max One alter you to kind purchases from marketing stores. This is why you should ever jazz what’s going on. deplorably, not all roughly ball. It is a big hand, spend a penny positive everyone has a secure that is particularly weighty for your make turn over endorse by applying the trend international, it is

  9. Google rolled out agame – Hummingbird was called by update overdue in 2013. It removed the ability proper to find out what keywords added people to an internet site Google confirmed that they meant it when they thought to stop building sites for keywords and alternatively, build them for users. The term, SEO, includes a new nowadays, meaning. Improve your entire Web profile and stop worrying about those keywords.

  10. Greate article. Keep writing such kind of info on your page. Im really impressed by it.
    Hi there, You have performed an incredible job. I’ll definitely digg it and personally suggest to my friends. I am sure they’ll be benefited from this site.

  11. Excellent items from you, man. I have take note your stuff prior to and you are just too excellent. I really like what you’ve received right here, really like what you are stating and the way in which through which you are saying it. You make it entertaining and you still take care of to keep it wise. I can not wait to learn much more from you. This is actually a terrific web site.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *