J'ai tendance à déclarer comme statiques toutes les méthodes d'une classe lorsque cette classe n'a pas besoin de suivre les états internes. Par exemple, si j'ai besoin de transformer A en B et que je ne compte pas sur un état interne C pouvant varier, je crée une transformation statique. S'il y a un état interne C que je veux pouvoir ajuster, j'ajoute un constructeur pour définir C et n'utilise pas de transformation statique.
J'ai lu diverses recommandations (y compris sur StackOverflow) de ne pas abuser des méthodes statiques mais je ne comprends toujours pas ce qui ne va pas avec la règle empirique ci-dessus.
Est-ce une approche raisonnable ou non?
Il existe deux types de méthodes statiques courantes:
Il existe quelques utilisations courantes de la statistique «non sécurisée» - par exemple, dans le modèle Singleton - mais sachez que, malgré tous les jolis noms que vous appelez, vous ne faites que transformer des variables globales. Réfléchissez bien avant d'utiliser des statiques dangereuses.
Un objet sans état interne est une chose suspecte.
Normalement, les objets encapsulent l'état et le comportement. Un objet qui n'encapsule que le comportement est étrange. Parfois, c'est un exemple de poids léger ou poids mouche .
D'autres fois, il s'agit d'une conception procédurale réalisée dans un langage objet.
Ce n’est qu’un suivi de la réponse de John Millikin.
Bien qu'il soit sans danger de rendre statique les méthodes sans état (qui sont en gros des fonctions), cela peut parfois conduire à un couplage difficile à modifier. Considérez que vous avez une méthode statique en tant que telle:
public class StaticClassVersionOne {
public static void doSomeFunkyThing(int arg);
}
Que vous appelez comme:
StaticClassVersionOne.doSomeFunkyThing(42);
Ce qui est très bien et très pratique, jusqu'à ce que vous rencontriez un cas dans lequel vous devez modifier le comportement de la méthode statique et constater que vous êtes étroitement lié à StaticClassVersionOne
. Vous pourriez peut-être modifier le code et tout irait bien, mais si d'autres appelants dépendent de l'ancien comportement, ils devront être pris en compte dans le corps de la méthode. Dans certains cas, ce corps de méthode peut devenir assez moche ou incontrôlable s’il tente d’équilibrer tous ces comportements. Si vous séparez les méthodes, vous devrez peut-être modifier le code à plusieurs endroits pour en tenir compte ou faire des appels à de nouvelles classes.
Mais considérez si vous avez créé une interface pour fournir la méthode et que vous la donnez aux appelants. Maintenant, lorsque le comportement doit changer, une nouvelle classe peut être créée pour implémenter l'interface, qui est plus propre, plus facilement testée et plus facilement maintenable. et cela à la place est donné aux appelants. Dans ce scénario, il n'est pas nécessaire de modifier ni même de recompiler les classes appelantes et les modifications sont localisées.
Ce n'est peut-être pas une situation probable, mais je pense que cela vaut la peine d'être examiné.
L'autre option consiste à les ajouter en tant que méthodes non statiques sur l'objet d'origine:
c'est-à-dire changer:
public class BarUtil {
public static Foo transform(Bar toFoo) { ... }
}
dans
public class Bar {
...
public Foo transform() { ...}
}
cependant, dans de nombreuses situations, cela n’est pas possible (par exemple, la génération de code de classe standard à partir de XSD/WSDL/etc), ou cela allongera la classe, et les méthodes de transformation peuvent souvent être un réel problème pour les objets complexes et vous les voulez simplement. dans leur propre classe séparée. Alors oui, j'ai des méthodes statiques dans les classes utilitaires.
Les classes statiques conviennent si elles sont utilisées aux bons endroits.
À savoir: les méthodes qui sont des méthodes "feuille" (elles ne modifient pas l'état, elles transforment simplement l'entrée en quelque sorte). De bons exemples de ceci sont des choses comme Path.Combine. Ces sortes de choses sont utiles et permettent la syntaxe de terser.
Les problèmes que j'ai avec la statique sont nombreux:
Premièrement, si vous avez des classes statiques, les dépendances sont masquées. Considérer ce qui suit:
public static class ResourceLoader
{
public static void Init(string _rootPath) { ... etc. }
public static void GetResource(string _resourceName) { ... etc. }
public static void Quit() { ... etc. }
}
public static class TextureManager
{
private static Dictionary<string, Texture> m_textures;
public static Init(IEnumerable<GraphicsFormat> _formats)
{
m_textures = new Dictionary<string, Texture>();
foreach(var graphicsFormat in _formats)
{
// do something to create loading classes for all
// supported formats or some other contrived example!
}
}
public static Texture GetTexture(string _path)
{
if(m_textures.ContainsKey(_path))
return m_textures[_path];
// How do we know that ResourceLoader is valid at this point?
var texture = ResourceLoader.LoadResource(_path);
m_textures.Add(_path, texture);
return texture;
}
public static Quit() { ... cleanup code }
}
En regardant TextureManager, vous ne pouvez pas savoir quelles étapes d’initialisation doivent être effectuées en regardant un constructeur. Vous devez vous plonger dans la classe pour trouver ses dépendances et initialiser les choses dans le bon ordre. Dans ce cas, le ResourceLoader doit être initialisé avant de s'exécuter. Augmentez maintenant ce cauchemar de dépendance et vous pouvez probablement deviner ce qui va arriver. Imaginez que vous essayez de maintenir le code sans ordre d’initialisation explicite. Comparez cela avec l'injection de dépendance avec les instances - dans ce cas, le code ne sera même pas compile si les dépendances ne sont pas remplies!
De plus, si vous utilisez des statistiques qui modifient l’état, c’est comme un château de cartes. On ne sait jamais qui a accès à quoi et le design a tendance à ressembler à un monstre spaghetti.
Enfin et surtout, l’utilisation de la statistique associe un programme à une implémentation spécifique. Le code statique est l'antithèse de la conception pour la testabilité. Tester un code qui est criblé de statique est un cauchemar. Un appel statique ne peut jamais être échangé pour un double test (sauf si vous utilisez des infrastructures de test spécialement conçues pour simuler des types statiques), de sorte qu'un système statique fait de tout ce qui l'utilise un test d'intégration instantané.
En bref, la statique convient pour certaines choses et pour les petits outils ou le code jetable, je ne découragerais pas leur utilisation. Cependant, au-delà de cela, ils sont un cauchemar sanglant pour la maintenabilité, la bonne conception et la facilité des tests.
Voici un bon article sur les problèmes: http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/
Cela semble être une approche raisonnable. La raison pour laquelle vous ne souhaitez pas utiliser trop de classes/méthodes statiques est que vous finissez par abandonner la programmation orientée objet et plus vers le domaine de la programmation structurée.
Dans votre cas, lorsque vous transformez simplement A en B, disons que tout ce que nous faisons est de transformer le texte pour passer de
"hello" =>(transform)=> "<b>Hello!</b>"
Une méthode statique aurait alors un sens.
Toutefois, si vous appelez fréquemment ces méthodes statiques sur un objet et que celui-ci a tendance à être unique pour de nombreux appels (par exemple, la façon dont vous l'utilisez dépend de l'entrée) ou s'il fait partie du comportement inhérent de l'objet, être sage de le faire partie de l'objet et en maintenir un état. Une façon de le faire serait de l'implémenter en tant qu'interface.
class Interface{
method toHtml(){
return transformed string (e.g. "<b>Hello!</b>")
}
method toConsole(){
return transformed string (e.g. "printf Hello!")
}
}
class Object implements Interface {
mystring = "hello"
//the implementations of the interface would yield the necessary
//functionality, and it is reusable across the board since it
//is an interface so... you can make it specific to the object
method toHtml()
method toConsole()
}
Edit: Les méthodes d'assistance HTML dans Asp.Net MVC ou Ruby sont un bon exemple d'utilisation intéressante des méthodes statiques. Ils créent des éléments HTML qui ne sont pas liés au comportement d'un objet et sont donc statiques.
Edit 2: Modification de la programmation fonctionnelle en programmation structurée (pour une raison quelconque, j'ai été dérouté), des accessoires à Torsten pour l'avoir signalé.
La raison pour laquelle vous êtes mis à l’écart des méthodes statiques est que leur utilisation annule l’un des avantages des objets. Les objets sont destinés à l'encapsulation de données. Cela évite les effets secondaires inattendus et évite les bugs. Les méthodes statiques n'ont pas de données encapsulées * et ne permettent donc pas d'obtenir cet avantage.
Cela dit, si vous n’utilisez pas les données internes, vous pourrez les utiliser et les exécuter un peu plus vite. Assurez-vous de ne pas toucher aux données globales qu’ils contiennent.
J'ai récemment refactoré une application pour supprimer/modifier certaines classes initialement implémentées en tant que classes statiques. Au fil du temps, ces classes ont énormément acquis et les utilisateurs ont continué à étiqueter les nouvelles fonctions comme statiques, puisqu’il n’y avait jamais eu d’instance flottante.
Donc, ma réponse est que les classes statiques ne sont pas intrinsèquement mauvaises, mais il serait peut-être plus facile de commencer à créer des instances maintenant, puis de refactoriser plus tard.
Je considérerais cela comme une odeur de design. Si vous utilisez principalement des méthodes statiques, vous n'avez probablement pas une très bonne conception OO. Ce n'est pas nécessairement mauvais, mais comme pour toutes les odeurs, cela me ferait arrêter et réévaluer. Cela laisse à penser que vous pourriez être en mesure de créer un meilleur OO, ou que vous devriez peut-être aller dans l'autre sens et éviter OO entièrement pour ce problème.
Eh bien, il n'y a pas de solution miracle, bien sûr. Les classes statiques sont acceptables pour les petits utilitaires/aides. Mais l’utilisation de méthodes statiques pour la programmation de la logique d’entreprise est certainement perverse. Considérons le code suivant
public class BusinessService
{
public Guid CreateItem(Item newItem, Guid userID, Guid ownerID)
{
var newItemId = itemsRepository.Create(createItem, userID, ownerID);
**var searchItem = ItemsProcessor.SplitItem(newItem);**
searchRepository.Add(searchItem);
return newItemId;
}
}
Vous voyez un appel de méthode statique à ItemsProcessor.SplitItem(newItem);
Ça sent la cause
BusinessService
en l'isolant de ItemsProcessor
(la plupart des outils de test ne simulent pas les classes statiques) et rend les tests unitaires impossibles. Pas de tests unitaires == basse qualitéJ'avais l'habitude d'aller et venir entre une classe avec un tas de méthodes statiques et un singleton. Les deux résolvent le problème, mais le singleton peut être beaucoup plus facilement remplacé par plus d'un. (Les programmeurs semblent toujours tellement certains qu’il n’y aura qu’un seul élément et je me suis trompé assez de fois pour abandonner complètement les méthodes statiques, sauf dans des cas très limités).
Quoi qu'il en soit, le singleton vous donne la possibilité de transmettre ultérieurement quelque chose à l’usine pour obtenir une instance différente, ce qui change le comportement de l’ensemble de votre programme sans refactoring. Transformer une classe globale de méthodes statiques en quelque chose avec des données de «sauvegarde» différentes ou un comportement légèrement différent (classe enfant) est un problème majeur.
Et les méthodes statiques ne présentent aucun avantage similaire.
Alors oui, ils sont mauvais.
Tant que pas l'état interne entre en jeu, c'est bien. Notez que les méthodes statiques sont généralement censées être thread-safe. Par conséquent, si vous utilisez des structures de données auxiliaires, utilisez-les de manière thread-safe.
Si c'est une méthode utilitaire, c'est bien de la rendre statique. Guava et Apache Commons sont construits sur ce principe.
Mon opinion à ce sujet est purement pragmatique. Si c'est votre code d'application, les méthodes statiques ne sont généralement pas la meilleure chose à avoir. Les méthodes statiques ont de sérieuses limitations en matière de tests unitaires - elles ne peuvent pas être facilement simulées: vous ne pouvez pas injecter une fonctionnalité statique simulée dans un autre test. En outre, vous ne pouvez généralement pas injecter de fonctionnalités dans une méthode statique.
Donc, dans ma logique d'application, j'ai généralement de petits appels de méthodes statiques similaires à des utilitaires. C'est à dire.
static cutNotNull(String s, int length){
return s == null ? null : s.substring(0, length);
}
l'un des avantages est que je ne teste pas de telles méthodes :-)
Les méthodes statiques sont généralement un mauvais choix, même pour du code sans état. Créez plutôt une classe singleton avec ces méthodes, qui est instanciée une fois et injectée dans les classes qui souhaitent utiliser les méthodes. De telles classes sont plus faciles à se moquer et à tester. Ils sont beaucoup plus orientés objet. Vous pouvez les envelopper avec un proxy en cas de besoin. La statique rend OO beaucoup plus difficile et je ne vois aucune raison de les utiliser dans presque tous les cas. Pas à 100% mais presque tous.
Si vous savez que vous aurez jamais besoin d'utiliser l'état interne de C, c'est bon. Si cela devait changer dans le futur, cependant, vous devriez rendre la méthode non statique. Si ce n'est pas statique pour commencer, vous pouvez simplement ignorer l'état interne si vous n'en avez pas besoin.