Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Le Singleton et le Dependency Injection pattern en PHP
Par tse_jc

Le , par tse_jc

0PARTAGES

Bonjour,

Voici un vaste sujet auquel je m'attaque ici, sujet qui fait débat surtout sur les blogs anglophones, parfois houleux, j'ai même lu un développeur qui disait "je ne parle plus aux développeurs qui n'utilisent pas d'injecteur de dépendance dans leur développements, pour moi c'est une nécessité professionnelle de progression." (cf: Exposé en anglais sur l'injecteur de dépendance et le singleton, source de cette déclaration)

Avant de commenter un tel propos et de recadrer et pondérer les choses de la manière la plus objective que possible et sans parti pris, voici ce que la communauté professionnelle a à reprocher au design pattern singleton de manière générale et récurrente:
- Il a un scope global un peu comme une variable globale;
- Il rends les tests unitaires difficiles voire impossibles.
- Il oblige à utiliser un couplage fort avec ses dépendances, les cache même fortement, limitant l'évolutivité et l'extensibilité de ces dépendances, rendant une telle tâche ardue et difficilement maintenable.
- Certains pensent même qu'il n'y a aucune utilité à n'avoir qu'une seule instance d'une classe possible dans une application PHP.

Donc juste avant de répondre et d'argumenter, je pense qu'il est important de préciser qu'il n'y a pas de bon ou de mauvais pattern de développement. Ils ont en effet tous leur utilité et leur spécificité ainsi que leur avantages et leur inconvénients (il n'existe pas de solution universelle parfaite).
Comment faire ses choix dans un tel contexte? La réponse doit d'abord être issue de la maîtrise d'ouvrage, qui elle seule est en mesure d'établir l'architecture applicative adaptée au cahier des charges lui même défini en fonction des prérequis métiers et des contextes d'exploitation. Il se doit d'être objectif et neutre (sans parti pris) pour rester efficace et pertinent.

A partir de ces bases, il nous paraît déjà évident que les objections citées en début de billet, restent subjectives car hors d'un tel contexte. Mais ne nous contentons pas d'une telle observation et allons un peu plus loin dans l'étude des besoins d'une application web.

Tout d'abord, une application web est avant tout une application dite n-tiers, c'est-à-dire répartie sur au moins 3 "supports" ou plus : de façon minimaliste on a de façon usuelle et générale:
1) Le serveur base de données, qui est un serveur relationnel et transactionné, capable de traiter de l'information en parrallèle et qui sait tirer profit automatiquement des ressources (CPU,RAM,Disque) dont il dispose.
2) Le serveur tiers, en général un serveur (objet) gérant les requêtes client http/https (apache,IIS,...). Son rôle étant <strong>à minima</strong>, en plus de gérer les requêtes http/https clientes, d'identifier le client, de gérer et sécuriser son accès à l'applicatif, de mettre en mémoire cache des données, et de délivrer au client les données demandées mises en forme conformément au cahier des charges.
3) Le client, lieu où réside l'IHM de l'applicatif et où l'homme peut interagir avec les données qui sont mises à sa disposition.

Que peut-on dire d'une telle architecture?
1) Sa définition sémantique "n-tiers" oblige à dire que c'est une application dont les éléments constitutifs sont répartis et donc décentralisés, contrairement à une application classique compilée (comme le serait par exemple une application Windows : word,etc..)
2) Ainsi respecter une telle architecture afin d'optimiser ses spécificités c'est aussi décentraliser les compétences et spécialiser les tâches applicatives au niveau de chaque tiers : gestion et pilotage des données au niveau SGBDR, contrôle des accès et des requêtes applicatives au niveau de apache,IIS et PHP (ou autre) et donc au niveau du serveur objet, gestion de l'interface au niveau client.
3) rendre performant une telle architecture c'est rendre autonome chaque tiers applicatif de manière à ce que la seule dépendance restante existante entre chaque tiers soit celle des données, et de faire en sorte d'optimiser la bande passante en ne requêtant pas des données qui ont déjà été délivrées.
4) Respecter l'ensemble des points précédents, c'est s'assurer de la bonne capacité de montée en charge d'une telle architecture d'application.

Observations sur cette structure
- Le développement en base de données épaisses est à ma connaissance aujourd'hui le seul à répondre à l'ensemble de ces exigences structurellement parlant.
- L'ensemble des CMS et frameworks du marché ne le respectent pas et centralisent l'ensemble de la gestion applicative au niveau du serveur tiers et donc objet (PHP/Apache, etc...), utilisent le SGBDR comme un lieu dénormalisé (au sens des bases de données relationnelles) en faisant de la persistance de classe essentiellement. Là est la cause principale de leur difficulté à gérer la montée et la tenue de charge applicative.

Au niveau du serveur tiers en PHP, toujours dans l'esprit de monopoliser le moins de ressources possibles, quelles sont les entités minimales de gestion pour faire tourner une application web?
Avant de répondre à cela, il est important de préciser ici qu'il n'y a que deux contextes généraux applicatifs :
- Le mode non connecté (ou anonyme) qui corresponds à la consultation d'un site internet normal
- Le mode connecté, qui corresponds à l'accès sécurisé à un back office, front office, extranet, intranet,... représentant le contexte d'étude de ce billet.

En mode connecté donc, les entités minimales sont au nombre de 2 et peuvent être représentées par deux classes : utilisateur et connection. Ces deux classes ont les particularités suivantes:
- Elles restent indépendantes du modèle de données (qui peut être plus complexe comme dans un ERP par exemple).
- Elles sont indispensables au bon fonctionnement applicatif et ont besoin d'être chargées systématiquement.
- Elles ont un besoin à la fois d'être sécuritaire et de posséder une emprunte mémoire minimale, cahier des charges garanti en ne pouvant les instancier qu'une seule fois dans l'application, et en respectant une encapsulation totale de l'information.
- Elles représentent deux classes contrôleur dans un modèle MVC.
- Elles doivent jouer un rôle de cache pour les données qu'elles gèrent pour solliciter toujours à minima la base de données.
Conclusion => Le Singleton offre tous ces avantages et peut garantir ce cahier des charges.

Nous nous retrouvons donc dans un contexte tout autre que celui exposé dans le lien du début de ce post, lien qui expose le pourquoi et le côté incontournable du pattern d'injecteur de dépendances, dont le contexte présenté reste celui de l'architecture d'une application compilée et donc centralisée et qui se retrouve pour "une petite application" avec un registre de plus de 20 dépendances à charger au démarrage pour fonctionner avec des noms aussi long que le bras, dépendances qui peuvent dépasser les 200 sur de grosses applications, alors qu'ici on en a besoin que de deux +1 voire 2 (grosses applications) pour la gestion du module courant de consultation.

La différence est déjà démesurée, les ressources monopolisées non comparables...

En ce qui concerne les dépendances cachées par le singleton.
Dans une telle architecture web lorsque le modèle MVC est utilisé, les dépendances "cachées" par le contrôleur singleton ne sont pas un mystère : il ne peut y en avoir que 2 types possibles : une dépendance de vue et une dépendance de modèle. Ni plus, ni moins. L'argument qu'elles soient "cachées" n'en est de plus pas un, car il est très facile de les isoler au sein du contrôleur:
Code : Sélectionner tout
$reponse=$this->MODEL('udpate_sales_permonth',$params);
L'approche est déterministe et reste affectée dynamiquement à l'exécution, ce qui reste très facilement maintenable et extensible à souhait sans modification ni mise à jour structurelle du code. Dès qu'une nouvelle fonctionnalité doit être implémentée, il suffit de créer la classe modèle correspondante et de l'appeler via le contrôleur lorsque applicable. Tout reste isolé et facilement maintenable.

Le maître mot est de savoir adapter l'architecture de son application à ses contraintes d'exploitation et métier, et non pas faire de la technique pour de la technique, ni de vouloir imposer un modèle applicatif lorsque celui-ci n'est pas pertinent pour le besoin et les contraintes exprimées, pour une quelconque raison hors contexte.

Concernant le scope global du singleton
Pour commencer, contrairement à une variable globale qui est déclarée et qui monopolise des ressources d'une manière permanente, le singleton ne l'est que si il est instancié.
Concernant l'exploitation du singleton dans le contexte exposé dans ce billet, c'est-à-dire pouvant servir de cache, il est légitime que celui-ci puisse être accessible globalement dans l'application. Il faudra donc éviter pour cet usage de rendre la méthode publique __wakeup du singleton privée sous peine de désactiver la possibilité de l'utiliser comme cache pour les données qu'il contrôle, comme je l'ai déjà aperçu dans certains billets sur le net. Ensuite, je rappelle qu'un singleton peut être défini dans un namespace donné ce qui peut réduire son scope de définition, et bien qu'étant global à l'applicatif, il préserve l'encapsulation, ce qui reste essentiel pour un contrôleur.
Pour les développeurs issus du C, Java, etc... qui continuent à penser que d'avoir des objets globaux à l'application c'est pas propre, ou qui ont d'autres qualificatifs en tête, je trouve bizarre qu'ils tiennent de tels propos lorsqu'il sont les premiers (et à juste titre) à compiler leur bibliothèques de fonctions ou certains contrôleurs dans une dll et à déclarer celle-ci comme dépendance globale au sein de leur projet. Je reste à votre disposition pour en discuter plus en détail.

Concernant l'aspect "abstraction à la couche de données"
J'ai vu certains qui utilisent le pattern DI (Dependency Injection) pour créer une couche d'abstraction aux données, et qui par exemple font un code pour accéder aux données via PDO, un autre via mysqli, un autre via ODBC etc... C'est du n'importe quoi pour une seule raison :
=> PDO, mysqli, etc... représentent déjà à eux seuls une couche d'abstraction aux données. Il ne sert à rien de créer une abstraction sur de l'abstraction.
S'il est recommandé d'utiliser PDO avec PHP, certains en font fi. Pourtant on peut travailler avec PDO sur du SQL Server en installant le driver natif pour SQLServer, idem pour postgreSQL, pareil pour MySQL, etc...

De plus, Certains font comme tous les CMS et frameworks openSource du marché leur ont montré, et tuent les performances de leur application (en centralisant la gestion données au niveau tiers) en faisant/utilisant soit :
- un ORM pour accéder à leur données
- en développant eux-même un niveau d'abstraction idiot en construisant des requêtes génériques via des objets
Code : Sélectionner tout
$GET_ARTICLES->query('SELECT * FROM articles');

=> au lieu de le faire des bibliothèques métier par SGBDR appelant des procédures stockées ou vues sur le SGBDR, et en optimisant la mise en forme des paramètres en fonction du SGBDR ciblé (utilisation d'arrays sur postgreSQL, etc...) et en ne requêtant seulement les données demandées.

Voilà j'espère que ce petit tour d'horizon vous aura aidé à y voir plus clair.
Alors le Pattern Dependency Injector a ses raisons d'être, et dans une architecture web n-tiers de type MVC, reste à utiliser au cas par cas lorsque les contraintes l'exigent.

Bonne lecture à vous

Une erreur dans cette actualité ? Signalez-le nous !