Question

Disons que j’ai une classe appelée PermissionManager qui ne devrait exister qu’une seule fois pour mon système et qui remplit en principe la fonction de gestion des diverses autorisations pour diverses actions de mon application. Maintenant, j'ai une classe dans mon application qui doit pouvoir vérifier une autorisation donnée dans l'une de ses méthodes. Le constructeur de cette classe est actuellement public, c'est-à-dire utilisé par les utilisateurs de l'API.

Jusqu'à il y a quelques semaines, j'aurais simplement demandé à ma classe d'appeler le pseudo-code suivant quelque part:

     PermissionManager.getInstance().isReadPermissionEnabled(this)

Mais puisque j'ai remarqué que tout le monde ici détestait singletons + ce type de couplage, je me demandais quelle serait la meilleure solution, car les arguments que j'ai lus contre les singletons semblent avoir un sens (non testable, couplage élevé, etc.) .

Devrais-je donc obliger les utilisateurs de l'API à transmettre une instance de PermissionManager dans le constructeur de la classe? Même si je ne souhaite qu’une seule instance PermissionManager pour mon application?

Ou est-ce que je m'y prends mal et que je devrais avoir un constructeur non public et une usine quelque part qui passe dans l'instance de PermissionManager pour moi?

Informations complémentaires Notez que lorsque je dis "injection de dépendance", je parle de la DI Pattern ... Je n'utilise aucun framework DI comme Guice ou Spring. (... encore)

Était-ce utile?

La solution

Si vous utilisez un framework d'injection de dépendances, le moyen courant de le gérer consiste à transmettre un objet PermissionsManager dans le constructeur ou à avoir une propriété de type PermissionsManager que le framework définit pour vous.

Si cela n’est pas réalisable, il est judicieux de demander aux utilisateurs d’obtenir une instance de cette classe via factory. Dans ce cas, la fabrique passe le PermissionManager au constructeur lorsqu'il crée la classe. Lors du démarrage de votre application, vous devez d'abord créer un seul PermissionManager, puis créer votre fabrique en passant par le PermissionManager.

Vous avez raison de dire qu'il est généralement difficile pour les clients d'une classe de savoir où trouver l'instance PermissionManager correcte et de la transmettre (ou même de se soucier du fait que votre classe utilise un PermissionManager).

Une solution de compromis que j'ai vue consiste à attribuer à votre classe une propriété de type PermissionManager. Si la propriété a été définie (par exemple, dans un test unitaire), vous utilisez cette instance, sinon vous utilisez le singleton. Quelque chose comme:

PermissionManager mManager = null;
public PermissionManager Permissions
{
  if (mManager == null)
  {
    return mManager;
  }
  return PermissionManager.getInstance();
}

Bien entendu, à strictement parler, votre PermissionManager doit implémenter une sorte d'interface IPermissionManager, et c'est ce que devrait faire référence à votre autre classe afin qu'une implémentation factice puisse être remplacée plus facilement lors des tests.

Autres conseils

Vous pouvez en effet commencer par injecter le logiciel PermissionManager. Cela rendra votre classe plus testable.

Si cela pose des problèmes aux utilisateurs de cette classe, vous pouvez leur demander d'utiliser une méthode de fabrique ou une fabrique abstraite. Vous pouvez également ajouter un constructeur sans paramètre à appeler qui injecte le PermissionManager pendant que vos tests utilisent un autre constructeur que vous pouvez utiliser pour simuler le PermissionManager.

Découpler davantage vos classes les rend plus flexibles, mais peut également les rendre plus difficiles à utiliser. Cela dépend de la situation dont vous aurez besoin. Si vous ne possédez qu'un seul PermissionManager et n'avez aucun problème à tester les classes qui l'utilisent, il n'y a aucune raison d'utiliser DI. Si vous voulez que les gens puissent ajouter leur propre implémentation PermissionManager, alors le DI est la solution.

Si vous vous abonnez à l'injection de dépendance, quelle que soit la classe dont votre classe PermissionManager doit l'injecter en tant qu'instance d'objet. Le mécanisme qui contrôle son instanciation (pour appliquer la nature du singleton) fonctionne à un niveau supérieur. Si vous utilisez un framework d'injection de dépendance tel que Guice, il peut effectuer le travail de mise en application. Si vous effectuez le câblage de votre objet à la main, l’injection de dépendance favorise le code de regroupement qui instancie (le travail des nouveaux opérateurs) loin de votre logique d’entreprise.

Dans les deux cas, cependant, le classique "capital-S" Singleton est généralement considéré comme un anti-pattern dans le contexte d’une injection de dépendance.

Ces articles m'ont beaucoup éclairé par le passé:

  

Devrais-je donc obliger les utilisateurs de l'API à transmettre une instance de PermissionManager dans le constructeur de la classe? Même si je ne souhaite qu’une seule instance PermissionManager pour mon application?

Oui, c’est tout ce que vous devez faire. Qu'une dépendance soit un singleton / par requête / par thread ou une méthode fabrique est la responsabilité de votre conteneur et de votre configuration. Dans le monde .net, nous aurions idéalement une dépendance sur une interface IPermissionsManager afin de réduire davantage le couplage. Je suppose donc que cette pratique est également recommandée en Java.

Le modèle singleton n’est pas mauvais en soi. Ce qui le rend laid, c’est la façon dont il est couramment utilisé, car il exige de ne vouloir qu’une seule instance d’une certaine classe, ce qui, à mon avis, est une grave erreur.

Dans ce cas, PermissionManager deviendrait une classe statique, sauf si vous avez besoin que ce soit un type instanciable.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top