{"id":833,"date":"2014-03-12T15:30:33","date_gmt":"2014-03-12T13:30:33","guid":{"rendered":"http:\/\/www.lafabriquedecode.com\/blog\/?p=833"},"modified":"2019-01-28T14:32:08","modified_gmt":"2019-01-28T12:32:08","slug":"php-le-design-pattern-observateur","status":"publish","type":"post","link":"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/","title":{"rendered":"PHP : le design pattern Observateur"},"content":{"rendered":"<h1>Observateur, un design pattern comportemental<\/h1>\n<p>Apr\u00e8s avoir vu <a href=\"http:\/\/www.lafabriquedecode.com\/blog\/2014\/02\/php-le-design-pattern-adaptateur\/\" title=\"PHP : le design pattern Adaptateur\" target=\"_blank\">Adaptateur<\/a>, <a href=\"http:\/\/www.lafabriquedecode.com\/blog\/2013\/04\/php-un-exemple-simple-de-design-pattern-decorator\/\" title=\"PHP : un exemple simple de design pattern Decorator\" target=\"_blank\">D\u00e9corateur<\/a>, <a href=\"http:\/\/www.lafabriquedecode.com\/blog\/2013\/04\/php-un-exemple-simple-du-design-pattern-template-method\/\" title=\"PHP : un exemple simple du design pattern Template Method\" target=\"_blank\">Template Method<\/a> ou <a href=\"http:\/\/www.lafabriquedecode.com\/blog\/2013\/04\/le-design-pattern-factory-method-en-php\/\" title=\"Le design pattern Factory Method en PHP\" target=\"_blank\">Factory<\/a>, nous allons nous concentrer sur un <em>design pattern<\/em> comportemental : <strong>Observateur<\/strong>. Comme tous les design patterns comportementaux (au sens GoF du terme), <strong>Observateur<\/strong> d\u00e9crit la mani\u00e8re dont des objets interagissent entre eux.<\/p>\n<h1>Quels sont ces objets ?<\/h1>\n<p>Ce <em>design pattern<\/em> met en jeu deux types d&rsquo;objets : <\/p>\n<ul>\n<li>un sujet<\/li>\n<li>des observateurs<\/li>\n<\/ul>\n<p>Un <em>sujet<\/em> notifie un ou plusieurs <em>observateurs<\/em> qu&rsquo;un changement d&rsquo;\u00e9tat vient de se produire chez lui.<\/p>\n<div id=\"attachment_855\" style=\"width: 810px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/7-Jumelles-et-Lunettes-sans-correction.jpg\"><img aria-describedby=\"caption-attachment-855\" loading=\"lazy\" src=\"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/7-Jumelles-et-Lunettes-sans-correction.jpg\" alt=\"Personne observant le ciel aux jumelles\" width=\"800\" height=\"514\" class=\"size-full wp-image-855\" srcset=\"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/7-Jumelles-et-Lunettes-sans-correction.jpg 800w, http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/7-Jumelles-et-Lunettes-sans-correction-300x192.jpg 300w, http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/7-Jumelles-et-Lunettes-sans-correction-624x400.jpg 624w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/a><p id=\"caption-attachment-855\" class=\"wp-caption-text\">Un observateur attendant avec impatience une notification en provenance d&rsquo;un sujet.<\/p><\/div>\n<h1>Dans quel \u00e9tat j&rsquo;erre ?<\/h1>\n<p>Le sujet poss\u00e8de un \u00e9tat interne; lorsque cet \u00e9tat est alt\u00e9r\u00e9, il va notifier ses observateurs de ce changement. Prenons un exemple trivial, qui va parler au plus grand nombre : lorsqu&rsquo;une personne pr\u00e9sente sur un r\u00e9seau social (notre sujet) f\u00eate son anniversaire, son \u00e9tat change car sa propri\u00e9t\u00e9 \u00e2ge est incr\u00e9ment\u00e9 d&rsquo;une unit\u00e9 et quiconque suit ce sujet (un observateur, donc) re\u00e7oit une notification l&rsquo;avertissant de ce changement d&rsquo;\u00e9tat. L&rsquo;observateur se met \u00e0 jour et affiche la bonne nouvelle \u00e0 l&rsquo;\u00e9cran: \u00ab\u00a0X f\u00eate son anniversaire, n&rsquo;oubliez pas de lui souhaiter !\u00a0\u00bb&#8230;<\/p>\n<h1>Structure du design pattern Observateur<\/h1>\n<p>Voici la version telle que d\u00e9finie par le Gang of Four :<\/p>\n<p><a href=\"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/observer.gif\"><img loading=\"lazy\" src=\"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/observer.gif\" alt=\"Structure du design pattern Observateur\" width=\"615\" height=\"243\" class=\"alignnone size-full wp-image-873\" \/><\/a><\/p>\n<p>Toute la puissance de ce <em>design pattern<\/em> r\u00e9side dans le fait que sa structure, \u00e0 base d&rsquo;<em>abstractions<\/em>, induit un couplage faible entre les deux types d&rsquo;objets qui le composent : un sujet ne sait rien du type concret de ses observateurs, si ce n&rsquo;est qu&rsquo;il se conforme en tous points \u00e0 l&rsquo;interface <strong>Observer<\/strong>, pas plus qu&rsquo;il n&rsquo;est au courant de leur nombre. Le sujet n&rsquo;aura donc aucunement \u00e0 se risquer \u00e0 d&rsquo;hasardeuses hypoth\u00e8ses sur la nature de ceux qui l&rsquo;observent et n&rsquo;importe quel objet sera en mesure de l&rsquo;observer, du moment que la classe concr\u00e8te dont il est issu impl\u00e9mente l&rsquo;interface <strong>Observer<\/strong>.<\/p>\n<p>Lorsque j&rsquo;\u00e9voque ici la notion d&rsquo;interface, c&rsquo;est au sens large du terme et pas <em>stricto sensu<\/em>. A l&rsquo;origine &#8211; voil\u00e0 maintenant 20 ans &#8211; GoF pr\u00e9conisait l&rsquo;utilisation de classes abstraites, \u00e0 d\u00e9river en classes concr\u00e8tes. Mais vous ne le savez que trop bien, l&rsquo;h\u00e9ritage n&rsquo;est pas une panac\u00e9e universelle, loin s&rsquo;en faut.<\/p>\n<p>Quelques points cl\u00e9s de ce mod\u00e8le :<\/p>\n<ul>\n<li>Sujet connait l&rsquo;ensemble de ses observateurs. Leur nombre n&rsquo;est pas limit\u00e9.<\/li>\n<li>Les observateurs s&rsquo;enregistrent lorsqu&rsquo;ils veulent observer un sujet et s&rsquo;en d\u00e9tachent lorsqu&rsquo;ils ne le veulent plus.<\/li>\n<li>Lorsque son \u00e9tat est alt\u00e9r\u00e9, Sujet notifie l&rsquo;ensemble de ses observateurs, sans notion d&rsquo;ordre et sans distinction !<\/li>\n<li>Lorsqu&rsquo;il est notifi\u00e9, un observateur peut obtenir des informations en provenance du sujet. Il peut d\u00e9cider de g\u00e9rer une notification ou bien de l&rsquo;ignorer.<\/li>\n<li>Rien n&rsquo;interdit \u00e0 un observateur de suivre plusieurs sujets. Il faut \u00e9videmment que cet observateur puisse d\u00e9terminer l&rsquo;origine des notifications qu&rsquo;il re\u00e7oit.<\/li>\n<\/ul>\n<p>Un petit b\u00e9mol, toutefois : en l&rsquo;\u00e9tat actuel des choses, la fonction de mise \u00e0 jour de l&rsquo;observateur (<em>Update<\/em>) ne permet pas de savoir ce qui a chang\u00e9 dans le sujet, laissant \u00e0 la charge de l&rsquo;observateur la responsabilit\u00e9 de \u00ab\u00a0deviner\u00a0\u00bb ce qui a entrain\u00e9 une modification.<\/p>\n<h1>La mise \u00e0 jour, c\u00f4t\u00e9 observateur<\/h1>\n<p>Nous allons voir comment \u00e0 l&rsquo;autre bout du fil, les observateurs g\u00e8rent leur mise \u00e0 jour d&rsquo;\u00e9tat avec la fonction <em>Update<\/em>.<\/p>\n<h2>Tu tires ou tu pousses ?<\/h2>\n<p>Deux modes de fonctionnement s&rsquo;offrent \u00e0 nous :<\/p>\n<ul>\n<li>par pouss\u00e9e (push)<\/li>\n<li>par traction (pull)<\/li>\n<\/ul>\n<h2>Le mode push &#8211; \u00ab\u00a0Tiens, prends \u00e7a !\u00a0\u00bb<\/h2>\n<p>Le sujet pousse des informations aux observateurs. L&rsquo;inconv\u00e9nient de cette fa\u00e7on de faire est que le lien entre le sujet et ses observateurs se resserre. Le sujet peut se retrouver \u00e0 pousser des informations dont certains observateurs n&rsquo;auront pas besoin. Que pousser et \u00e0 qui ?<\/p>\n<h2>Le mode pull &#8211; \u00ab\u00a0Viens te servir !\u00a0\u00bb<\/h2>\n<p>Le sujet notifie simplement ses observateurs qu&rsquo;un changement vient d&rsquo;avoir lieu, \u00e0 eux de savoir ce qui a chang\u00e9 !<\/p>\n<h1>Des versions basiques d&rsquo;Observateur<\/h1>\n<h2>Les contrats \u00e0 respecter<\/h2>\n<p>Vous le savez maintenant, un sujet notifie N observateurs. On lui attache\/d\u00e9tache ces observateurs \u00e0 l&rsquo;aide des m\u00e9thodes appropri\u00e9es et il les informe d&rsquo;un changement via l&rsquo;appel \u00e0 la m\u00e9thode <em>mettreAJour<\/em> de chacun d&rsquo;entre eux. Voici \u00e0 quoi vont ressembler nos contrats d&rsquo;interface (dans la version GoF, ces abstractions sont des classes abstraites, ici nous utilisons des interfaces).<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\ninterface SujetInterface\r\n{\r\n    public function attacher(ObservateurInterface $observateur): void;\r\n    public function detacher(ObservateurInterface $observateur): void;\r\n    public function notifier(): void;\r\n}\r\n\r\ninterface ObservateurInterface\r\n{\r\n    public function mettreAJour(SujetInterface $sujet): void;\r\n}\r\n<\/pre>\n<h2>La notification en version pull<\/h2>\n<h3>Le sujet<\/h3>\n<p>Notre sujet stocke l&rsquo;ensemble de ses observateurs dans une variable d&rsquo;instance priv\u00e9e pr\u00e9vue \u00e0 cet effet. Ce tableau est rempli ou vid\u00e9 au fur et \u00e0 mesure que les observateurs sont attach\u00e9s ou d\u00e9tach\u00e9s. Un <em>setter<\/em> mettreAJourLesNouvelles est pr\u00e9vu pour modifier la variable d&rsquo;instance $nouvelles et un <em>getter<\/em> donnerLesNouvelles servira aux observateurs pour aller r\u00e9cup\u00e9rer le dernier \u00e9tat du sujet. Souvenez-vous qu&rsquo;en mode <strong>pull<\/strong>, ce sont les observateurs qui doivent se renseigner sur l&rsquo;\u00e9tat du sujet et \u00e9ventuellement mettre \u00e0 jour le leur avec ces informations <em>tir\u00e9es<\/em> depuis le sujet.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\nclass Sujet implements SujetInterface\r\n{\r\n    private $nouvelles;\r\n\r\n    private $observateurs;\r\n    \r\n    public function attacher(ObservateurInterface $observateur): void\r\n    {\r\n        $this-&gt;observateurs[] = $observateur;\r\n    }\r\n \r\n    public function detacher(ObservateurInterface $observateur): void\r\n    {\r\n        $key = array_search($observateur, $this-&gt;observateurs);\r\n \r\n        if (false !== $key) {\r\n            unset($this-&gt;observateurs[$key]);\r\n        }\r\n    }\r\n \r\n    public function notifier(): void \r\n    {\r\n        foreach ($this-&gt;observateurs as $observateur) {\r\n            $observateur-&gt;mettreAJour($this);\r\n        }\r\n    }\r\n\r\n    public function mettreAJourLesNouvelles(string $nouvelles): void \r\n    {\r\n        $this-&gt;nouvelles = $nouvelles;\r\n        $this-&gt;notifier();\r\n    }\r\n    \r\n    public function donnerLesNouvelles(): string \r\n    {\r\n        return $this-&gt;nouvelles;\r\n    }\r\n}\r\n<\/pre>\n<h3>L&rsquo;observateur<\/h3>\n<p>C&rsquo;est lui qui fait le travail, une fois qu&rsquo;il est notifi\u00e9 \u00e0 travers sa m\u00e9thode mettreAJour. Il re\u00e7oit le sujet \u00e0 l&rsquo;origine de la notification de mise \u00e0 jour car il peut observer plusieurs sujets; il nous faut donc imp\u00e9rativement savoir qui a chang\u00e9 parmi les objets qu&rsquo;on observe. Ici nous avons r\u00e9duit la structure de la classe \u00e0 sa plus simple expression: l&rsquo;observateur re\u00e7oit une notification de nouvelle de la part d&rsquo;un sujet, il va TIRER l&rsquo;information depuis ce sujet et mettre son \u00e9tat interne en accord avec celui du sujet, afin d&rsquo;avoir les toutes derni\u00e8res nouvelles.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\nclass Observateur implements ObservateurInterface\r\n{\r\n    private $dernieresNouvelles;\r\n    \r\n    public function mettreAJour(SujetInterface $sujet): void\r\n    {\r\n        $this-&gt;dernieresNouvelles = $sujet-&gt;donnerLesNouvelles();\r\n    }\r\n}\r\n<\/pre>\n<h3>Le code client<\/h3>\n<p>Voici le code pour utiliser cette structure en mode <strong>pull<\/strong>, il est tr\u00e8s simple: nous cr\u00e9ons deux observateurs que nous attachons \u00e0 notre unique sujet et nous mettons \u00e0 jour l&rsquo;\u00e9tat de notre sujet, qui va d\u00e9clencher deux notifications (une pour chaque observateur).<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n$sujet = new Sujet();\r\n$observateur = new Observateur();\r\n$autreObservateur = new Observateur();\r\n$sujet-&gt;attacher($observateur);\r\n$sujet-&gt;attacher($autreObservateur);\r\n$sujet-&gt;mettreAJourLesNouvelles(&quot;La bourse d\u00e9visse !&quot;);\r\n<\/pre>\n<h2>La notification en version push<\/h2>\n<h3>Changement d&rsquo;interface<\/h3>\n<p>En mode <strong>push<\/strong>, c&rsquo;est le sujet qui pousse les donn\u00e9es modifi\u00e9es \u00e0 ses observateurs. Il sait donc ce qu&rsquo;il leur faut, ce qui induit un couplage plus serr\u00e9 que dans le mode <strong>pull<\/strong>. Il peut aussi leur passer son \u00e9tat complet mais attention si les informations sont volumineuses&#8230;Les observateurs ont-ils besoin de recevoir un sac postal complet alors qu&rsquo;ils attendent une carte postale ?<\/p>\n<p>Quoiqu&rsquo;il en soit, l&rsquo;interface de l&rsquo;observateur change et donc l&rsquo;appel de la m\u00e9thode aussi, dans le sujet:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\ninterface ObservateurInterface\r\n{\r\n    public function mettreAJour(SujetInterface $sujet, array $donnees): void;\r\n}\r\n<\/pre>\n<h3>Le sujet<\/h3>\n<p>Le sujet fera d\u00e9sormais:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n    public function notifier(): void \r\n    {\r\n        foreach ($this-&gt;observateurs as $observateur) {\r\n            $observateur-&gt;mettreAJour($this, [&quot;nouvelles&quot; =&gt; $this-&gt;nouvelles]);\r\n        }\r\n    }\r\n<\/pre>\n<h3>L&rsquo;observateur<\/h3>\n<p>L&rsquo;observateur ira quant \u00e0 lui se servir \u00e0 ladite cl\u00e9 dans le tableau $donnees, qui dans la pratique sera \u00e9videmment bien plus fourni:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n    public function mettreAJour(SujetInterface $sujet, array $donnees): void\r\n    {\r\n        $this-&gt;dernieresNouvelles = $donnees[&quot;nouvelles&quot;];\r\n    }\r\n<\/pre>\n<p>Le code client ne bougera pas d&rsquo;un pouce, c&rsquo;est la cuisine interne de l&rsquo;objet qui change tr\u00e8s l\u00e9g\u00e8rement. Le principe est rigoureusement identique !<\/p>\n<h1>Notre version d&rsquo;Observateur<\/h1>\n<p>Qu&rsquo;est-ce qui interdit \u00e0 nos sujets d&rsquo;\u00eatre des observateurs d&rsquo;autre sujets ? Rien !<br \/>\nNous allons simuler un r\u00e9seau social.<\/p>\n<h2>Nos interfaces<\/h2>\n<p>Elles ne changent pas beaucoup des pr\u00e9c\u00e9dentes:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\ninterface SujetInterface\r\n{\r\n    public function attacher(ObservateurInterface $observateur): void;\r\n    public function detacher(ObservateurInterface $observateur): void;\r\n    public function notifier(): void;\r\n}\r\n\r\ninterface SuiveurInterface\r\n{\r\n    public function suivre(SujetInterface $sujet): void;\r\n    public function nePlusSuivre(SujetInterface $sujet): void;\r\n}\r\n\r\ninterface ObservateurInterface\r\n{\r\n    public function mettreAJour(SujetInterface $sujet): void;\r\n}\r\n<\/pre>\n<p>Nous avons juste rajout\u00e9 une interface <em>SuiveurInterface<\/em> qui liste ce que doit savoir faire un suiveur: suivre et cesser de suivre.<\/p>\n<h2>Le membre<\/h2>\n<p>Dans notre r\u00e9seau social, nous avons deux types de membres: des personnes et des entreprises ou des associations qui ont des pages. Ces deux cat\u00e9gories de membres peuvent suivre des membres, comme sur Facebook. Pouvant \u00eatre \u00e0 la fois sujet et observateur, ils doivent impl\u00e9menter l&rsquo;ensemble des interfaces. Nous factorisons les comportements dans une classe abstraite qui va contenir pour un membre, les membres qu&rsquo;il suit et les membres qui le suivent. Les membres suivis iront dans <strong>$sujets<\/strong> et les suiveurs, dans <strong>$observateurs<\/strong>.<\/p>\n<p>Faisons simple: de base, un membre a juste un nom et nous mettons un <em>getter<\/em> dessus, m\u00eame pas de <em>setter<\/em>.<br \/>\nVous constatez que la m\u00e9thode <em>mettreAJour<\/em> ne contient que le sujet, la notification se fera donc en mode <strong>pull<\/strong>.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\nabstract class AbstractMembre implements SujetInterface, SuiveurInterface, ObservateurInterface\r\n{\r\n    protected $nom;\r\n    \r\n    protected $observateurs;\r\n    \r\n    protected $sujets;\r\n    \r\n    public function __construct(string $nom)\r\n    {\r\n        $this-&gt;nom = $nom;\r\n    }\r\n    \r\n    public function attacher(ObservateurInterface $observateur): void\r\n    {\r\n        echo $this-&gt;donnerNom().PHP_EOL;\r\n        echo &quot;\\t&quot;.$observateur-&gt;donnerNom().&quot; vous \u00e0 ajout\u00e9 \u00e0 ses contacts&quot;.PHP_EOL;\r\n        $this-&gt;observateurs[] = $observateur;\r\n    }\r\n \r\n    public function detacher(ObservateurInterface $observateur): void\r\n    {\r\n        $key = array_search($observateur, $this-&gt;observateurs);\r\n \r\n        if (false !== $key) {\r\n            echo $this-&gt;donnerNom().PHP_EOL;\r\n            unset($this-&gt;observateurs[$key]);\r\n            echo &quot;\\tVous avez enlev\u00e9 &quot;.$observateur-&gt;donnerNom().&quot; de vos contacts&quot;.PHP_EOL;\r\n        }\r\n    }\r\n \r\n    public function notifier(): void \r\n    {\r\n        foreach ($this-&gt;observateurs as $observateur) {\r\n            $observateur-&gt;mettreAJour($this);\r\n        }\r\n    }\r\n\r\n    public function donnerNom(): string\r\n    {\r\n        return $this-&gt;nom;\r\n    }\r\n    \r\n    public function suivre(SujetInterface $sujet): void\r\n    {\r\n        \/\/ pour \u00e9viter qu'un membre puisse s'auto-suivre\r\n        if ($sujet === $this) {\r\n            return;\r\n        }\r\n        \r\n        $this-&gt;sujets[$sujet-&gt;donnerNom()] = clone $sujet;\r\n        $sujet-&gt;attacher($this);\r\n    }\r\n    \r\n    public function nePlusSuivre(SujetInterface $sujet): void\r\n    {\r\n        echo $this-&gt;donnerNom().PHP_EOL;\r\n    \r\n        $key = array_search($sujet, $this-&gt;sujets);\r\n \r\n        if (false !== $key) {\r\n            unset($this-&gt;sujets[$key]);\r\n        }\r\n        \r\n        echo &quot;\\tVous ne suivez plus &quot;.$sujet-&gt;donnerNom().PHP_EOL;\r\n    }\r\n    \r\n    public function mettreAJour(SujetInterface $sujet): void\r\n    {\r\n        echo $this-&gt;donnerNom().PHP_EOL;\r\n        $nom = $sujet-&gt;donnerNom();\r\n        \r\n        if (array_key_exists($nom, $this-&gt;sujets)) {\r\n            $sujetStocke = $this-&gt;sujets[$nom];\r\n            \r\n            if ($sujet instanceof PageCommerciale) {\r\n                $urlMagasin = $sujet-&gt;donnerUrlMagasin();\r\n                $urlMagasinStocke = $sujetStocke-&gt;donnerUrlMagasin();\r\n                \r\n                if ($urlMagasin !== $urlMagasinStocke) {\r\n                    echo &quot;\\tLe site web de $nom vaut d\u00e9sormais $urlMagasin&quot;;\r\n                }\r\n\r\n            } elseif ($sujet instanceof Membre) {\r\n                $age = $sujet-&gt;donnerAge();\r\n                $ageStocke = $sujetStocke-&gt;donnerAge();\r\n\r\n                if ($ageStocke !== $age) {\r\n                    echo &quot;\\t$nom f\u00eate son anniversaire, il a $age ans&quot;;\r\n                    $this-&gt;sujets[$nom] = clone $sujet;\r\n                }\r\n                \r\n                $hobbies = $sujet-&gt;donnerHobbies();\r\n                $hobbiesStocke = $sujetStocke-&gt;donnerHobbies();\r\n\r\n                if ($hobbiesStocke !== $hobbies) {\r\n                    echo &quot;\\t$nom a de nouveaux hobbies: &quot;.implode(&quot;, &quot;, $hobbies);\r\n                    $this-&gt;sujets[$nom] = clone $sujet;\r\n                }\r\n            }\r\n            echo PHP_EOL;\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<h2>Les classes concr\u00e8tes<\/h2>\n<p>On en compte deux, une pour les membres et une pour les pages commerciales. Un membre a un \u00e2ge et des hobbies, une page commerciale ne comporte que l&rsquo;URL du site marchand. L\u00e0 aussi j&rsquo;ai vraiment gard\u00e9 l&rsquo;essentiel dans chaque classe, qui est sur-simplifi\u00e9e. Voici notre classe Membre:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\nfinal class Membre extends AbstractMembre {\r\n \r\n    private $age;\r\n     \r\n    private $hobbies;\r\n     \r\n    public function changerAge(int $age): void {\r\n        $this-&gt;age = $age;\r\n        $this-&gt;notifier();\r\n    }\r\n     \r\n    public function changerHobbies(array $hobbies): void {\r\n        $this-&gt;hobbies = $hobbies;\r\n        $this-&gt;notifier();\r\n    }\r\n \r\n    public function donnerAge(): ?int {\r\n        return $this-&gt;age;\r\n    }\r\n    \r\n    public function donnerHobbies(): ?array {\r\n        return $this-&gt;hobbies;\r\n    }\r\n}\r\n<\/pre>\n<p>et voici notre classe PageCommerciale:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\nfinal class PageCommerciale extends AbstractMembre {\r\n \r\n    private $urlMagasin;\r\n     \r\n    public function changerUrlMagasin(string $urlMagasin): void {\r\n        $this-&gt;urlMagasin = $urlMagasin;\r\n        $this-&gt;notifier();\r\n    }\r\n \r\n    public function donnerUrlMagasin(): ?string {\r\n        return $this-&gt;urlMagasin;\r\n    }\r\n}\r\n<\/pre>\n<p>Chaque classe comporte des <em>setters<\/em> qui vont modifier l&rsquo;\u00e9tat des objets et donc g\u00e9n\u00e9rer des notifications aux objets qui les observent.<\/p>\n<h2>Retour sur la m\u00e9thode mettreAJour<\/h2>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n    public function mettreAJour(SujetInterface $sujet): void\r\n    {\r\n        echo $this-&gt;donnerNom().PHP_EOL;\r\n        $nom = $sujet-&gt;donnerNom();\r\n        \r\n        if (array_key_exists($nom, $this-&gt;sujets)) {\r\n            $sujetStocke = $this-&gt;sujets[$nom];\r\n            \r\n            if ($sujet instanceof PageCommerciale) {\r\n                $urlMagasin = $sujet-&gt;donnerUrlMagasin();\r\n                $urlMagasinStocke = $sujetStocke-&gt;donnerUrlMagasin();\r\n                \r\n                if ($urlMagasin !== $urlMagasinStocke) {\r\n                    echo &quot;\\tLe site web de $nom vaut d\u00e9sormais $urlMagasin&quot;;\r\n                }\r\n\r\n            } elseif ($sujet instanceof Membre) {\r\n                $age = $sujet-&gt;donnerAge();\r\n                $ageStocke = $sujetStocke-&gt;donnerAge();\r\n\r\n                if ($ageStocke !== $age) {\r\n                    echo &quot;\\t$nom f\u00eate son anniversaire, il a $age ans&quot;;\r\n                    $this-&gt;sujets[$nom] = clone $sujet;\r\n                }\r\n                \r\n                $hobbies = $sujet-&gt;donnerHobbies();\r\n                $hobbiesStocke = $sujetStocke-&gt;donnerHobbies();\r\n\r\n                if ($hobbiesStocke !== $hobbies) {\r\n                    echo &quot;\\t$nom a de nouveaux hobbies: &quot;.implode(&quot;, &quot;, $hobbies);\r\n                    $this-&gt;sujets[$nom] = clone $sujet;\r\n                }\r\n            }\r\n            echo PHP_EOL;\r\n        }\r\n    }\r\n<\/pre>\n<p>Cette m\u00e9thode est au niveau de la classe abstraite; elle fait une v\u00e9rification sur le type concret des sujets car les informations \u00e0 tirer depuis ce sujet ne seront pas les m\u00eames pour les observateurs concrets. Vous voyez arriver le probl\u00e8me si nous rajoutons des classes concr\u00e8tes en bas de l&rsquo;arbre d&rsquo;h\u00e9ritage, votre <em>if<\/em> va enfler et il vous faudra peut-\u00eatre avoir recours \u00e0 un nouveau design pattern pour g\u00e9rer ces diff\u00e9rentes fa\u00e7ons d&rsquo;op\u00e9rer selon les types concrets, lequel ? <\/p>\n<p>Notez que nous clonons les objets; il nous faut <em>fossiliser<\/em> leur \u00e9tat \u00e0 l&rsquo;instant t pour pouvoir le comparer avec celui du sujet re\u00e7u en param\u00e8tre de la m\u00e9thode.<\/p>\n<h2>Le code client<\/h2>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n$magasin = new PageCommerciale('ACME Web Store');\r\n$star = new Membre('Rasmus Lerdorf');\r\n$copain = new Membre('Jean-Michel Apeuprey');\r\n$autreStar = new Membre('Novak Djokovic');\r\n$membre = new Membre('S\u00e9bastien Ferrandez');\r\n\r\n$membre-&gt;suivre($magasin);\r\n$membre-&gt;suivre($star);\r\n$membre-&gt;suivre($autreStar);\r\n$membre-&gt;suivre($copain);\r\n$copain-&gt;suivre($membre);\r\n$magasin-&gt;suivre($autreStar);\r\n$copain-&gt;changerAge(41);\r\n$membre-&gt;changerHobbies([&quot;guitare&quot;]);\r\n$star-&gt;changerAge(36);\r\n$star-&gt;changerHobbies([&quot;linux&quot;, &quot;c++&quot;, &quot;c&quot;]);\r\n$autreStar-&gt;changerHobbies([&quot;musculation&quot;, &quot;karaok\u00e9&quot;]);\r\n$magasin-&gt;changerUrlMagasin('http:\/\/www.acmestore.com');\r\n\/\/ Je n'aime plus le tennis !\r\n$membre-&gt;nePlusSuivre($autreStar);\r\n\/\/ Je me suis f\u00e2ch\u00e9 avec Jean-Michel, il n'est plus mon ami !\r\n$membre-&gt;detacher($copain);\r\n<\/pre>\n<h2>TADAAAM !<\/h2>\n<p>Voici la sortie g\u00e9n\u00e9r\u00e9e par l&rsquo;ex\u00e9cution du code client, on n&rsquo;est pas trop mal on dirait ! N&rsquo;h\u00e9sitez pas \u00e0 proposer des alternatives ou poser des questions dans les commentaires !<\/p>\n<p><a href=\"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/Screenshot_20190128_131155.png\"><img loading=\"lazy\" src=\"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/Screenshot_20190128_131155.png\" alt=\"Sortie \u00e9cran pour les observateurs\" width=\"666\" height=\"600\" class=\"alignnone size-full wp-image-1148\" srcset=\"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/Screenshot_20190128_131155.png 666w, http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/Screenshot_20190128_131155-300x270.png 300w, http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/Screenshot_20190128_131155-624x562.png 624w\" sizes=\"(max-width: 666px) 100vw, 666px\" \/><\/a><\/p>\n<h1>O\u00f9 trouve t-on des impl\u00e9mentations d&rsquo;Observateur ?<\/h1>\n<p>Typiquement, dans la programmation par \u00e9v\u00e9nements o\u00f9 des classes souscrivent aupr\u00e8s d&rsquo;une classe gestionnaire d&rsquo;\u00e9v\u00e8nements qui les informera lorsque l&rsquo;\u00e9v\u00e8nement auquel elles ont souscrit se produit (en appelant parfois des fonctions de <em>callback<\/em>).<\/p>\n<p>Lorsque vous cliquez sur un article pour l&rsquo;ajouter et que votre panier se met \u00e0 jour, vous avez un bel exemple d&rsquo;Observateur.<\/p>\n<p>Sur les r\u00e9seaux sociaux, comme dans notre exemple trivial, nous recevons des notifications d\u00e8s qu&rsquo;un \u00e9v\u00e9nement d&rsquo;une personne que l&rsquo;on suit se produit (changement de statut, nouveau <em>post<\/em> etc.)<\/p>\n<p>En PHP, la SPL propose des classes pour mettre en place le <a href=\"http:\/\/www.php.net\/manual\/en\/class.splsubject.php\" target=\"_blank\">design pattern<\/a> Observateur avec SPLObserver.<\/p>\n<p>Enfin dans Symfony, les \u00e9v\u00e8nements sont g\u00e9r\u00e9s selon ce principe&#8230;Les observateurs sont des <em>listeners<\/em> ou des <em>event subscribers<\/em> auquel on <em>dispatche<\/em> des notifications contenant des <em>events<\/em>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Observateur, un design pattern comportemental Apr\u00e8s avoir vu Adaptateur, D\u00e9corateur, Template Method ou Factory, nous allons nous concentrer sur un design pattern comportemental : Observateur. Comme tous les design patterns comportementaux (au sens GoF du terme), Observateur d\u00e9crit la mani\u00e8re dont des objets interagissent entre eux. Quels sont ces objets ? Ce design pattern met [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[24,40,14,3,29],"tags":[43,60,73,74],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.6.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>PHP : le design pattern Observateur - La Fabrique de code - Tech blog<\/title>\n<meta name=\"description\" content=\"Ce design pattern induit une relation de type un-\u00e0-plusieurs entre des objets : un sujet notifie plusieurs observateurs pouvant \u00eatre de types diff\u00e9rents.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"PHP : le design pattern Observateur - La Fabrique de code - Tech blog\" \/>\n<meta property=\"og:description\" content=\"Ce design pattern induit une relation de type un-\u00e0-plusieurs entre des objets : un sujet notifie plusieurs observateurs pouvant \u00eatre de types diff\u00e9rents.\" \/>\n<meta property=\"og:url\" content=\"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/\" \/>\n<meta property=\"og:site_name\" content=\"La Fabrique de code - Tech blog\" \/>\n<meta property=\"article:published_time\" content=\"2014-03-12T13:30:33+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-01-28T12:32:08+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/7-Jumelles-et-Lunettes-sans-correction.jpg\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary\" \/>\n<meta name=\"twitter:creator\" content=\"@LaFabrique2Code\" \/>\n<meta name=\"twitter:site\" content=\"@LaFabrique2Code\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/\",\"url\":\"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/\",\"name\":\"PHP : le design pattern Observateur - La Fabrique de code - Tech blog\",\"isPartOf\":{\"@id\":\"http:\/\/www.lafabriquedecode.com\/blog\/#website\"},\"datePublished\":\"2014-03-12T13:30:33+00:00\",\"dateModified\":\"2019-01-28T12:32:08+00:00\",\"author\":{\"@id\":\"http:\/\/www.lafabriquedecode.com\/blog\/#\/schema\/person\/83863c048b82fd9ccf6407bddd241162\"},\"description\":\"Ce design pattern induit une relation de type un-\u00e0-plusieurs entre des objets : un sujet notifie plusieurs observateurs pouvant \u00eatre de types diff\u00e9rents.\",\"breadcrumb\":{\"@id\":\"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"http:\/\/www.lafabriquedecode.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PHP : le design pattern Observateur\"}]},{\"@type\":\"WebSite\",\"@id\":\"http:\/\/www.lafabriquedecode.com\/blog\/#website\",\"url\":\"http:\/\/www.lafabriquedecode.com\/blog\/\",\"name\":\"La Fabrique de code - Tech blog\",\"description\":\"PHP objet, MySQL, Design Patterns, OOP...et plus !\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"http:\/\/www.lafabriquedecode.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Person\",\"@id\":\"http:\/\/www.lafabriquedecode.com\/blog\/#\/schema\/person\/83863c048b82fd9ccf6407bddd241162\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"http:\/\/www.lafabriquedecode.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"http:\/\/0.gravatar.com\/avatar\/fc2e1de7c8a1871b50ff9c6a6f8682a2?s=96&d=retro&r=g\",\"contentUrl\":\"http:\/\/0.gravatar.com\/avatar\/fc2e1de7c8a1871b50ff9c6a6f8682a2?s=96&d=retro&r=g\",\"caption\":\"admin\"},\"url\":\"http:\/\/www.lafabriquedecode.com\/blog\/author\/admin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"PHP : le design pattern Observateur - La Fabrique de code - Tech blog","description":"Ce design pattern induit une relation de type un-\u00e0-plusieurs entre des objets : un sujet notifie plusieurs observateurs pouvant \u00eatre de types diff\u00e9rents.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/","og_locale":"fr_FR","og_type":"article","og_title":"PHP : le design pattern Observateur - La Fabrique de code - Tech blog","og_description":"Ce design pattern induit une relation de type un-\u00e0-plusieurs entre des objets : un sujet notifie plusieurs observateurs pouvant \u00eatre de types diff\u00e9rents.","og_url":"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/","og_site_name":"La Fabrique de code - Tech blog","article_published_time":"2014-03-12T13:30:33+00:00","article_modified_time":"2019-01-28T12:32:08+00:00","og_image":[{"url":"http:\/\/www.lafabriquedecode.com\/blog\/wp-content\/uploads\/2014\/03\/7-Jumelles-et-Lunettes-sans-correction.jpg"}],"author":"admin","twitter_card":"summary","twitter_creator":"@LaFabrique2Code","twitter_site":"@LaFabrique2Code","twitter_misc":{"\u00c9crit par":"admin","Dur\u00e9e de lecture estim\u00e9e":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/","url":"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/","name":"PHP : le design pattern Observateur - La Fabrique de code - Tech blog","isPartOf":{"@id":"http:\/\/www.lafabriquedecode.com\/blog\/#website"},"datePublished":"2014-03-12T13:30:33+00:00","dateModified":"2019-01-28T12:32:08+00:00","author":{"@id":"http:\/\/www.lafabriquedecode.com\/blog\/#\/schema\/person\/83863c048b82fd9ccf6407bddd241162"},"description":"Ce design pattern induit une relation de type un-\u00e0-plusieurs entre des objets : un sujet notifie plusieurs observateurs pouvant \u00eatre de types diff\u00e9rents.","breadcrumb":{"@id":"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/"]}]},{"@type":"BreadcrumbList","@id":"http:\/\/www.lafabriquedecode.com\/blog\/2014\/03\/php-le-design-pattern-observateur\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"http:\/\/www.lafabriquedecode.com\/blog\/"},{"@type":"ListItem","position":2,"name":"PHP : le design pattern Observateur"}]},{"@type":"WebSite","@id":"http:\/\/www.lafabriquedecode.com\/blog\/#website","url":"http:\/\/www.lafabriquedecode.com\/blog\/","name":"La Fabrique de code - Tech blog","description":"PHP objet, MySQL, Design Patterns, OOP...et plus !","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"http:\/\/www.lafabriquedecode.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"fr-FR"},{"@type":"Person","@id":"http:\/\/www.lafabriquedecode.com\/blog\/#\/schema\/person\/83863c048b82fd9ccf6407bddd241162","name":"admin","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"http:\/\/www.lafabriquedecode.com\/blog\/#\/schema\/person\/image\/","url":"http:\/\/0.gravatar.com\/avatar\/fc2e1de7c8a1871b50ff9c6a6f8682a2?s=96&d=retro&r=g","contentUrl":"http:\/\/0.gravatar.com\/avatar\/fc2e1de7c8a1871b50ff9c6a6f8682a2?s=96&d=retro&r=g","caption":"admin"},"url":"http:\/\/www.lafabriquedecode.com\/blog\/author\/admin\/"}]}},"_links":{"self":[{"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/posts\/833"}],"collection":[{"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/comments?post=833"}],"version-history":[{"count":98,"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/posts\/833\/revisions"}],"predecessor-version":[{"id":1152,"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/posts\/833\/revisions\/1152"}],"wp:attachment":[{"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/media?parent=833"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/categories?post=833"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.lafabriquedecode.com\/blog\/wp-json\/wp\/v2\/tags?post=833"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}