J'ai passé les dernières heures à lire sur l'utilisation des classes static
et à essayer de savoir si je devrais les utiliser ou non, mais je n'ai toujours pas tiré de conclusion. Il semble que l'argument pourrait aller dans les deux sens. Dans mon application, j'ai créé ce que j'appelle des "classes d'assistance" qui contient des méthodes qui effectueront des tâches très courantes pour moi et qui seront appelées via mon application (ASP.Net MVC
Application Web utilisant C#
) et la question simple est, devraient-ils vraiment être statiques ou non?
Voici un exemple d'un de mes assistants.
public static class ActiveDirectoryHelper
{
public static PrincipalContext GetPrincipalContext(string ouName)
{
var fullOUName = string.Concat("OU=", ouName,",DC=");
return new PrincipalContext(ContextType.Domain, "", fullOUName, ConfigurationManager.AppSettings["ServiceAccountUser"], ConfigurationManager.AppSettings["ServiceAccountPassword"]);
}
public static PrincipalSearcher GetAllUsersInOU(string ouName)
{
var principalContext = GetPrincipalContext(ouName);
var userPrincipal = new UserPrincipal(principalContext);
return new PrincipalSearcher(userPrincipal);
}
public static UserPrincipal GetUserPrincipal(string userName, string ouName)
{
var principalContext = GetPrincipalContext(ouName);
return UserPrincipal.FindByIdentity(principalContext, userName);
}
}
Je pense que les classes statiques sont agréables dans de nombreux cas. Je pourrais simplement dire "utilisez-les quand c'est utile" mais ce n'est pas très utile.
Ma règle d'or est (comme toujours): est-ce que je cache des dépendances? Si vous vous sentez comme une "classe d'aide" est utile (bien que méfiez-vous de la faible cohésion), alors allez-y. Assurez-vous simplement que vos méthodes n'accèdent pas à l'état global. Les méthodes statiques pures sont belles. Les méthodes statiques qui dépendent de certains globaux ou qui ouvrent des connexions DB ou lisent à partir du disque/d'un fichier de configuration sont des bombes à retardement.
Ils rendent le test de l'application très difficile (la seule façon de le faire sera d'exécuter manuellement l'ensemble du système, ou avec des tests automatiques complets et fragiles, vous n'obtiendrez aucune granularité). Ils rendent également impossible l'échange de mises en œuvre.
N'oubliez pas le principe d'inversion de dépendance et le principe ouvert/fermé! Assurez-vous que vos classes sont connectables. Assurez-vous qu'ils ne tirent pas les choses de l'air. Si vous faites cela, alors allez-y et créez autant de méthodes statiques que vous le souhaitez!
Il semble que vous cachez une dépendance externe à Active Directory à l'aide de cette classe statique. Un problème ici est que si vous essayez de tester à l'unité la classe qui appelle ces méthodes, vous ne pouvez pas simuler des appels statiques. Vous inhibez donc immédiatement la testabilité de votre code. Je refactoriserais ceci à une interface, quelque chose comme IProvideActiveDirectoryInformation, et une classe concrète, ActiveDirectoryInformationProvider. Passez ensuite l'interface dans le constructeur de votre contrôleur. Cela vous permettra de câbler la classe de béton avec un conteneur DI. Il vous permettra également de simuler ActiveDirectoryInformationProvider et de retourner tout ce que vous voulez pour les méthodes d'interface.
Je voudrais également éviter de renvoyer des choses comme l'objet PrincipalSearcher dans l'interface. Si votre méthode est GetAllUsersInOU (), je m'attendrais à une liste d'utilisateurs ou de noms d'utilisateurs.
Une classe doit être statique si elle n'existe qu'en tant que concept abstrait dans votre application.
Par exemple, supposons que vous créez un clone de Twitter. Vous pouvez avoir 2 types de tweets, des tweets utilisateur et des publicités. Ils partagent tous deux un comportement commun mais sont différents. Ainsi, vous souhaitez utiliser le polymorphisme et une fabrique pour créer l'un ou l'autre.
Ces 2 classes de tweets doivent être des classes concrètes, car ce sont de vraies entités. Votre domaine est défini par ces classes.
L'usine doit être statique car elle n'existe qu'à un niveau abstrait pour améliorer la conception de votre application et pour vous aider à réutiliser le code qui créera un type de Tweet. Votre domaine n'est défini à aucun niveau par cette usine.
Donc, si vous ne pensez pas qu'une classe devrait jamais être instanciée mais n'a pas besoin d'être étendue pour être utilisée, c'est probablement un bon signe qu'elle devrait être statique.
L'une des choses suggérées dans La programmation orientée objet est mauvaise (le titre est d'attirer votre attention et le contenu est contesté mais divertissant) est que *Handler
, *Manager
et *Doer
les classes en général sont une odeur de code qui indique que quelqu'un essaie de forcer l'orientation d'objet sur un problème mieux adapté à une implémentation procédurale.
En C #, vous pouvez utiliser des classes statiques comme espaces de noms de module pour les procédures et de préférence les nommer comme tels. Je pense que c'est la façon de procéder, mais pas dans votre cas particulier.
Les applications de votre implémentation particulière auront des dépendances comportementales de l'état global (par exemple, les autorisations d'un utilisateur particulier), et en tant que telles devraient être une dépendance injectée afin que le code qui l'utilise puisse être testé de manière isolée.
Je manque peut-être quelque chose, mais à mon avis, la réponse se résume aux méthodes et aux variables de membre.
Si elles sont toutes statiques, la classe elle-même peut (et doit être) rendue statique. Sinon, ce n'est pas une classe statique.
N.B. il n'y a rien qui oblige la classe à être statique même si les méthodes et les variables sont toutes statiques.
Considérez le langage de programmation que vous utilisez (C #) comme un ensemble d'outils.
Il n'y a pas de règles, pas de "devrait" ou "ne devrait pas".
Regardez le travail que vous faites et choisissez le meilleur outil pour le travail.
Si vous avez besoin d'une collection de méthodes disponibles globalement, la même pour tous les threads, les classes statiques sont un bon outil. Si vous souhaitez stocker des données dans la classe (différentes pour chaque thread), elles ne le sont probablement pas.
Il n'y a rien de mal dans votre classe Helper. Je dois avouer que j'utilise ce genre de tils, Helpers ou Holders pour rendre accessibles certaines ressources/logiques de différents endroits.
Cependant, un architecte de mon entreprise me répète sans cesse que ces classes rassemblent souvent du code que je ne sais pas où placer. Il n'aime pas non plus, car vous pourriez apporter des composants à partir de couches auxquelles les couches supérieures/inférieures ne devraient pas avoir accès et ils effectuent des tâches hors de leurs responsabilités. Il apporte également quelques problèmes au moment du test.
Ça ne me dérange pas. Je les utilise et je les utilise aussi bien que je pense qu'il devrait être utilisé. Et ça marche bien. Alors ne t'inquiète pas.
Il est vrai qu'au moment des tests unitaires, vous aurez du mal à vous en moquer. Mais cela prendra un peu plus de temps pour le faire. Il y a des bibliothèques qui passent par ce problème.
Une autre approche pourrait vous empêcher de ce genre de lacunes (lors des tests). Par exemple, motif Singleton. Injection de l'instance en tant que dépendance de composant et conversion de méthodes en méthodes d'instance.
Quoi qu'il en soit, jusqu'à présent, je vois, vos solutions me vont bien.