web-dev-qa-db-fra.com

Réflexions et meilleures pratiques sur les classes statiques et les membres

Je suis très curieux de connaître les réflexions et les meilleures pratiques de l'industrie concernant les membres statiques ou des classes statiques entières. Y a-t-il des inconvénients à cela, ou participe-t-il à des anti-patterns?

Je vois ces entités comme des "classes/membres utilitaires", dans le sens où elles ne nécessitent ni n'exigent l'instanciation de la classe pour fournir la fonctionnalité.

Quelles sont les réflexions générales et les meilleures pratiques de l'industrie à ce sujet?

Consultez les exemples de classes et de membres ci-dessous pour illustrer ce à quoi je fais référence.

// non-static class with static members
//
public class Class1
{
    // ... other non-static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

// static class
//
public static class Class2
{
    // ... other static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

Merci d'avance!

11
Thomas Stringer

En général, évitez la statique - en particulier toute sorte d'état statique.

Pourquoi?

  1. La statique cause des problèmes avec la concurrence. Puisqu'il n'y a qu'une seule instance, elle est naturellement partagée entre l'exécution simultanée. Ces ressources partagées sont le fléau de la programmation simultanée, et souvent n'ont pas besoin d'être partagées.

  2. La statique cause des problèmes avec les tests unitaires. Tout framework de tests unitaires qui en vaut la peine exécute des tests simultanés. Ensuite, vous tombez sur # 1. Pire encore, vous compliquez vos tests avec tous les éléments de configuration/démontage, et le piratage dans lequel vous allez tomber pour essayer de "partager" le code de configuration.

Il y a aussi plein de petites choses. La statique a tendance à être inflexible. Vous ne pouvez pas les interfacer, vous ne pouvez pas les remplacer, vous ne pouvez pas contrôler le timing de leur construction, vous ne pouvez pas bien les utiliser dans les génériques. Vous ne pouvez pas vraiment les versionner.

Il y a certainement des utilisations de la statique: les valeurs constantes fonctionnent très bien. Les méthodes pures qui ne correspondent pas à une seule classe peuvent très bien fonctionner ici.

Mais en général, évitez-les.

18
Telastyn

Si la fonction est "pure", je ne vois aucun problème. Une fonction pure ne fonctionne que dans les paramètres d'entrée et fournit un résultat basé sur cela. Cela ne dépend d'aucun état global ou contexte externe.

Si je regarde votre propre exemple de code:

public class Class1
{
    public static string GetSomeString()
    {
        // do something
    }
}

Cette fonction ne prend aucun paramètre. Ainsi, elle n'est probablement pas pure (la seule implémentation pure de cette fonction serait de renvoyer une constante). Je suppose que cet exemple n'est pas représentatif de votre problème réel, je souligne simplement que ce n'est probablement pas une fonction pure.

Prenons un exemple différent:

public static bool IsOdd(int number) { return (number % 2) == 1; }

Il n'y a rien de mal à ce que cette fonction soit statique. Nous pourrions même en faire une fonction d'extension, permettant au code client de devenir encore plus lisible. Les fonctions d'extension sont fondamentalement juste un type spécial de fonctions statiques.

Telastyn mentionne correctement la concurrence comme un problème potentiel avec les membres statiques. Cependant, puisque cette fonction n'utilise pas l'état partagé, il n'y a pas de problème de concurrence ici. Un millier de threads peuvent appeler cette fonction simultanément sans aucun problème de concurrence.

Dans le framework .NET, les méthodes d'extension existent depuis un certain temps. LINQ contient de nombreuses fonctions d'extension (par exemple Enumerable.Where () , Enumerable.First () =, Enumerable.Single () , etc.). Nous ne les considérons pas comme mauvais, n'est-ce pas?

Les tests unitaires peuvent souvent bénéficier lorsque le code utilise des abstractions remplaçables, ce qui permet au test unitaire de remplacer le code système par un double de test. Les fonctions statiques interdisent cette flexibilité, mais cela est surtout important aux limites des couches architecturales, où nous voulons remplacer, par exemple, une couche d'accès aux données réelle par une fausse couche d'accès aux données.

Cependant, lors de l'écriture d'un test pour un objet qui se comporte différemment, selon qu'un nombre impair ou pair, nous n'avons pas vraiment besoin de pouvoir remplacer la fonction IsOdd() par une implémentation alternative. De même, je ne vois pas quand nous devons fournir une implémentation de Enumerable.Where() différente à des fins de test.

Examinons donc la lisibilité du code client pour cette fonction:

Option a (avec la fonction déclarée comme méthode d'extension):

public void Execute(int number) {
    if (number.IsOdd())
        // Do something
}

Option b:

public void Execute(int number) {
    var helper = new NumberHelper();
    if (helper.IsOdd(number))
        // Do something
}

La fonction statique (extension) rend le premier morceau de code beaucoup plus lisible et la lisibilité est très importante, utilisez donc les fonctions statiques le cas échéant.

17
Pete