Nous savons tous à quel point les singletons sont mauvais parce qu'ils cachent des dépendances et pour d' autres raisons .
Mais dans un framework, il peut y avoir de nombreux objets qui ne doivent être instanciés qu'une seule fois et appelés de partout (logger, db, etc.).
Pour résoudre ce problème, on m'a dit d'utiliser un soi-disant "Objects Manager" (ou Service Container comme symfony) qui stocke en interne toutes les références aux Services (enregistreur, etc.).
Mais pourquoi un fournisseur de services n'est-il pas aussi mauvais qu'un pur Singleton?
Le fournisseur de services masque également les dépendances et termine simplement la création de la première istance. J'ai donc vraiment du mal à comprendre pourquoi nous devrions utiliser un fournisseur de services au lieu de singletons.
PS. Je sais que pour ne pas cacher les dépendances, je devrais utiliser DI (comme indiqué par Misko)
Ajouter
J'ajouterais: ces jours-ci, les singletons ne sont pas si maléfiques, le créateur de PHPUnit l'a expliqué ici:
DI + Singleton résout le problème:
<?php
class Client {
public function doSomething(Singleton $singleton = NULL){
if ($singleton === NULL) {
$singleton = Singleton::getInstance();
}
// ...
}
}
?>
c'est assez intelligent même si cela ne résout pas du tout tous les problèmes.
À part DI et Service Container, existe-t-il une bonne solution acceptable pour accéder à ces objets d'aide?
la source
Réponses:
Service Locator est juste le moindre de deux maux pour ainsi dire. Le «moindre» se résumant à ces quatre différences ( du moins, je ne peux penser à aucune autre pour le moment ):
Principe de responsabilité unique
Le conteneur de services ne viole pas le principe de responsabilité unique comme le fait Singleton. Les singletons mélangent la création d'objets et la logique métier, tandis que le conteneur de services est strictement responsable de la gestion des cycles de vie des objets de votre application. À cet égard, Service Container est meilleur.
Couplage
Les singletons sont généralement codés en dur dans votre application en raison des appels de méthode statiques, ce qui conduit à des dépendances étroitement couplées et difficiles à simuler dans votre code. Le SL en revanche n'est qu'une classe et il peut être injecté. Ainsi, bien que tous vos classifiés en dépendent, au moins c'est une dépendance faiblement couplée. Donc, à moins que vous n'implémentiez le ServiceLocator en tant que Singleton lui-même, c'est un peu mieux et aussi plus facile à tester.
Cependant, toutes les classes utilisant le ServiceLocator dépendront désormais du ServiceLocator, qui est également une forme de couplage. Cela peut être atténué en utilisant une interface pour ServiceLocator afin que vous ne soyez pas lié à une implémentation concrète de ServiceLocator, mais vos classes dépendront de l'existence d'une sorte de localisateur alors que le fait de ne pas utiliser de ServiceLocator augmente considérablement la réutilisation.
Dépendances cachées
Le problème du masquage des dépendances existe cependant. Lorsque vous injectez simplement le localisateur dans vos classes consommatrices, vous ne connaîtrez aucune dépendance. Mais contrairement au Singleton, le SL instanciera généralement toutes les dépendances nécessaires dans les coulisses. Ainsi, lorsque vous récupérez un Service, vous ne vous retrouvez pas comme Misko Hevery dans l'exemple de CreditCard , par exemple , vous n'avez pas à instancier toutes les dépendances des dépendances à la main.
Récupérer les dépendances à l'intérieur de l'instance enfreint également la loi de Demeter , qui stipule que vous ne devez pas fouiller dans les collaborateurs. Une instance ne doit parler qu'à ses collaborateurs immédiats. Il s'agit d'un problème avec Singleton et ServiceLocator.
État mondial
Le problème de l'état global est également quelque peu atténué car lorsque vous instanciez un nouveau localisateur de service entre les tests, toutes les instances créées précédemment sont également supprimées (sauf si vous avez fait l'erreur et les avez enregistrées dans des attributs statiques dans le SL). Cela n'est vrai pour aucun état global dans les classes gérées par le SL, bien sûr.
Voir également Fowler sur Service Locator vs Dependency Injection pour une discussion beaucoup plus approfondie.
Une note sur votre mise à jour et l'article lié de Sebastian Bergmann sur le test de code qui utilise des Singletons : Sebastian ne suggère en aucun cas que la solution de contournement proposée rend l'utilisation de Singleons moins problématique. C'est juste une façon de rendre le code qu'il serait autrement impossible de tester plus testable. Mais c'est toujours du code problématique. En fait, il note explicitement: "Juste parce que vous pouvez, ne signifie pas que vous devriez".
la source
Le modèle de localisation de service est un anti-modèle. Cela ne résout pas le problème de l'exposition des dépendances (vous ne pouvez pas dire en regardant la définition d'une classe quelles sont ses dépendances car elles ne sont pas injectées, au lieu de cela, elles sont retirées du localisateur de services).
Votre question est donc la suivante: pourquoi les localisateurs de services sont-ils bons? Ma réponse est: ils ne le sont pas.
Évitez, évitez, évitez.
la source
Le conteneur de services masque les dépendances comme le fait le modèle Singleton. Vous voudrez peut-être suggérer d'utiliser des conteneurs d'injection de dépendances à la place, car il présente tous les avantages du conteneur de services mais aucun (à ma connaissance) des inconvénients du conteneur de services.
Pour autant que je sache, la seule différence entre les deux est que dans le conteneur de services, le conteneur de services est l'objet injecté (masquant ainsi les dépendances), lorsque vous utilisez DIC, le DIC injecte les dépendances appropriées pour vous. La classe gérée par le DIC est complètement inconsciente du fait qu'elle est gérée par un DIC, vous avez donc moins de couplage, des dépendances claires et des tests unitaires heureux.
C'est une bonne question à SO expliquant la différence des deux: Quelle est la différence entre les modèles d'injection de dépendances et de localisateur de service?
la source
Parce que vous pouvez facilement remplacer des objets dans Service Container par
1) héritage (la classe Object Manager peut être héritée et les méthodes peuvent être remplacées)
2) modification de la configuration (dans le cas de Symfony)
Et les singletons sont mauvais non seulement à cause d'un couplage élevé, mais parce qu'ils sont des _tons simples . C'est une mauvaise architecture pour presque toutes sortes d'objets.
Avec DI 'pure' (dans les constructeurs), vous paierez un très gros prix - tous les objets doivent être créés avant d'être passés dans le constructeur. Cela signifiera plus de mémoire utilisée et moins de performances. De plus, l'objet ne peut pas toujours être simplement créé et passé dans le constructeur - une chaîne de dépendances peut être créée ... Mon anglais n'est pas assez bon pour en discuter complètement, lisez-le dans la documentation Symfony.
la source
Pour moi, j'essaie d'éviter les constantes globales, les singletons pour une raison simple, il y a des cas où je pourrais avoir besoin de faire fonctionner des API.
Par exemple, j'ai front-end et admin. À l'intérieur de l'administrateur, je veux qu'ils puissent se connecter en tant qu'utilisateur. Considérez le code dans admin.
Cela peut établir une nouvelle connexion à la base de données, un nouvel enregistreur, etc. pour l'initialisation du frontend et vérifier si l'utilisateur existe réellement, valide, etc. Il utiliserait également des services de cookies et de localisation séparés appropriés.
Mon idée de singleton est - Vous ne pouvez pas ajouter deux fois le même objet à l'intérieur du parent. Par exemple
vous laisserait une seule instance et les deux variables pointant vers elle.
Enfin, si vous souhaitez utiliser le développement orienté objet, travaillez avec des objets, pas avec des classes.
la source
$api
var autour de votre framework? Je n'ai pas vraiment compris ce que tu veux dire. De plus, si l'appeladd('Logger')
renvoie la même instance, vous avez essentiellement un cotainer de service