Supposons qu'une règle (ou une règle empirique, de toute façon), a été imposée dans mon environnement de codage que toute méthode sur une classe qui n'utilise pas, ne modifie pas ou n'a pas besoin de variables d'instance pour faire son travail, soit rendue statique. Existe-t-il un temps de compilation, un temps d'exécution ou tout autre inconvénient inhérent à cette opération?
(modifié pour plus de précisions)
Je sais que la question était quelque peu ouverte et vague, alors je m'en excuse. Mon intention en demandant était dans le contexte de méthodes principalement "d'aide". Classes utilitaires (avec des CTOR privés afin qu'elles ne puissent pas être instanciées) en tant que détenteurs de méthodes statiques que nous faisons déjà. Ma question ici était plus en ligne avec ces petites méthodes qui aident la classe principale API.
Je pourrais avoir 4 ou 5 principales méthodes API/instance sur une classe qui font le vrai travail, mais au cours de cette opération, elles partagent certaines fonctionnalités communes qui peuvent uniquement travailler sur les paramètres d'entrée de la méthode API, et non sur l'état interne . Ce sont les sections de code que j'extrait généralement dans leurs propres méthodes d'assistance, et si elles n'ont pas besoin d'accéder à l'état de la classe, les rendre statiques.
Ma question était donc: est-ce intrinsèquement une mauvaise idée, et si oui, pourquoi? (Ou pourquoi pas?)
Le principal inconvénient est que vous ne pouvez pas échanger, remplacer ou choisir des implémentations de méthode au moment de l'exécution.
À mon avis, il y a quatre raisons d'éviter les méthodes statiques en Java. Cela ne veut pas dire que les méthodes statiques sont jamais applicables, mais seulement qu'elles doivent généralement être évitées.
Comme d'autres l'ont souligné, les méthodes statiques ne peuvent pas être simulées dans un test unitaire. Si une classe dépend, par exemple, de DatabaseUtils.createConnection()
, alors cette classe dépendante, et toutes les classes qui en dépendent, seront presque impossibles à tester sans avoir réellement une base de données ou une sorte d'indicateur "testing" dans DatabaseUtils
. Dans ce dernier cas, il semble que vous ayez en fait deux implémentations d'une interface DatabaseConnectionProvider
- voir le point suivant.
Si vous avez une méthode statique, son comportement s'applique à toutes les classes, partout. La seule façon de modifier son comportement de manière conditionnelle est de passer un indicateur en tant que paramètre à la méthode ou de définir un indicateur statique quelque part. Le problème avec la première approche est qu'elle change la signature de chaque appelant et devient rapidement encombrante à mesure que de plus en plus d'indicateurs sont ajoutés. Le problème avec la deuxième approche est que vous vous retrouvez avec du code comme celui-ci partout:
boolean oldFlag = MyUtils.getFlag (); MyUtils.someMethod (); MyUtils.setFlag (oldFlag);
Apache Commons Lang est un exemple de bibliothèque commune qui a rencontré ce problème: voir StringUtilsBean et ainsi de suite.
Les objets sont chargés une fois par ClassLoader, ce qui signifie que vous pourriez avoir plusieurs copies de vos méthodes statiques et variables statiques autour sans le savoir, ce qui peut causer des problèmes. Cela n'a généralement pas autant d'importance avec les méthodes d'instance, car les objets sont éphémères.
Si vous avez des méthodes statiques qui font référence à des variables statiques, celles-ci restent en place pendant toute la durée de vie du chargeur de classe et ne sont jamais récupérées. Si ceux-ci accumulent des informations (par exemple des caches) et que vous ne faites pas attention, vous pouvez rencontrer des "fuites de mémoire" dans votre application. Si vous utilisez des méthodes d'instance à la place, les objets ont tendance à être de plus courte durée et sont donc récupérés après un certain temps. Bien sûr, vous pouvez toujours avoir des fuites de mémoire avec des méthodes d'instance aussi! Mais c'est moins un problème.
J'espère que cela pourra aider!
L'avantage de performance est probablement négligeable. Utilisez des méthodes statiques pour tout ce qui ne dépend pas de l'état. Cela clarifie le code, comme vous pouvez le voir immédiatement avec un appel de méthode statique qu'aucun état d'instance n'est impliqué.
J'aime beaucoup cette question car c'est un point dont je discute depuis 4 ans dans ma vie professionnelle. La méthode statique a beaucoup de sens pour les classes qui ne portent aucun état. Mais récemment, j'ai été quelque peu révisé.
Les classes utilitaires ayant des méthodes statiques sont une bonne idée.
Les classes de service portant la logique métier peuvent être apatrides dans de nombreux cas. Au début, j'y ai toujours ajouté des méthodes statiques, mais quand j'ai acquis plus de familiarité avec le framework Spring (et une lecture plus générale), j'ai réalisé que ces méthodes devenaient impossibles à tester en tant qu'unité indépendante car vous ne pouvez pas injecter facilement de services fictifs dans cette classe. Par exemple. Une méthode statique appelant une autre méthode statique dans une autre classe, il n'y a aucun moyen que le test JUnit puisse court-circuiter ce chemin en injectant une implémentation factice au moment de l'exécution.
Je me suis donc en quelque sorte habitué à l'idée que les méthodes statiques utilitaires qui n'ont pas besoin d'appeler d'autres classes ou méthodes peuvent être statiques. Mais les classes de service en général doivent être non statiques. Cela vous permet de tirer parti des fonctionnalités des POO telles que la substitution.
Le fait d'avoir une classe d'instance singleton nous aide également à faire en sorte qu'une classe comme une classe statique utilise toujours les concepts OOP.
Disadvantage -> Static
Les membres font partie de la classe et restent donc en mémoire jusqu'à la fin de l'application et ne peuvent jamais être récupérés. L'utilisation d'un nombre excessif de membres statiques prédit parfois que vous ne parvenez pas à concevoir votre produit et à essayer de faire face à la programmation statique/procédurale. Cela indique que la conception orientée objet est compromise, ce qui peut entraîner un débordement de mémoire.
Tout est une question de contexte. Certaines personnes ont déjà donné des exemples où la statique est absolument préférable, comme lors de l'écriture de fonctions utilitaires sans état imaginable. Par exemple, si vous écrivez une collection d'algorithmes de tri différents à utiliser sur les tableaux, rendre votre méthode tout sauf statique perturbe simplement la situation. N'importe quel programmeur lisant votre code devrait demander pourquoi vous ne l'avez PAS rendu statique et devrait voir si vous faites quelque chose avec état pour l'objet.
public class Sorting {
public static void quiksort(int [] array) {}
public static void heapsort(int[] array) { }
}
Cela dit, il y a beaucoup de gens qui écrivent du code d'une certaine sorte, et insistent sur le fait qu'ils ont un code spécial unique, pour découvrir plus tard que ce n'est pas le cas. Par exemple, vous souhaitez calculer des statistiques sur une variable. Vous écrivez donc:
public class Stats {
public static void printStats(float[] data) { }
}
Le premier élément de mauvaise conception ici est que le programmeur a simplement l'intention d'imprimer les résultats, plutôt que de les utiliser de manière générique. L'incorporation d'E/S dans le calcul est terrible pour la réutilisation. Cependant, le problème suivant est que cette routine à usage général devrait calculer le max, le min, la moyenne, la variance, etc. et le stocker quelque part. Où? Dans l'état d'un objet. S'il s'agissait vraiment d'un élément unique, vous pourriez le rendre statique, mais bien sûr, vous allez trouver que vous voulez calculer la moyenne de deux choses différentes, puis c'est terriblement agréable si vous pouvez simplement instancier l'objet plusieurs fois .
public class Stats {
private double min,max,mean,var;
public void compute(float data[]) { ... }
public double getMin() { return min; }
public double
}
La réaction de genou contre l'électricité statique est souvent la réaction des programmeurs à la stupidité de faire ce genre de chose statiquement, car il est plus facile de simplement dire de ne jamais faire cela que d'expliquer réellement quels cas sont ok et lesquels sont stupides.
Notez que dans ce cas, j'utilise en fait l'objet comme une sorte de passe spéciale par référence, parce que Java est tellement désagréable à cet égard. En C++, ce genre de chose pourrait avoir été une fonction, avec n'importe quel état passé en référence. Mais même en C++, les mêmes règles s'appliquent, c'est juste que Java nous oblige à utiliser davantage les objets à cause du manque de passage par référence.
En ce qui concerne les performances, la plus grande augmentation des performances de la commutation à partir d'une méthode régulière est en fait d'éviter la vérification polymorphe dynamique qui est la valeur par défaut en Java et qui en C++ est spécifiée manuellement avec virtual.
Lorsque j'ai essayé la dernière fois, il y avait un avantage de 3: 1 d'appeler une méthode finale par rapport à une méthode régulière, mais aucun discernable pour appeler des fonctions statiques sur final.
Notez que si vous appelez une méthode à partir d'une autre, le JIT est souvent assez intelligent pour incorporer le code, auquel cas il n'y a aucun appel du tout, c'est pourquoi faire une déclaration sur exactement combien vous économisez est extrêmement dangereux. Tout ce que vous pouvez dire, c'est que lorsque le compilateur doit appeler une fonction, cela ne peut pas faire de mal s'il peut en appeler une comme statique ou finale qui nécessite moins de calcul.
Le principal problème auquel vous pouvez être confronté est que vous ne pourrez pas fournir une nouvelle implémentation si nécessaire.
Si vous avez encore des doutes (que votre implémentation puisse changer à l'avenir ou non), vous pouvez toujours utiliser une instance privée en dessous avec l'implémentation réelle:
class StringUtil {
private static StringUtil impl = new DefaultStringUtil();
public static String nullOrValue( String s ) {
return impl.doNullOrValue();
}
... rest omitted
}
Si pour "certains" raison, vous devez changer la classe d'implémentation que vous pouvez proposer:
class StringUtil {
private static StringUtil impl = new ExoticStringUtil();
public static String nullOrValue( String s ) {
return impl.doNullOrValue(s);
}
... rest omitted
}
Mais peut être excessif dans certaines circonstances.
Pour appeler les méthodes statiques, vous n'avez pas besoin de créer d'objets de classe. La méthode est disponible immédiatement.
En supposant que la classe est déjà chargée. Sinon, il y a un peu d'attente. :-)
Je pense que l'électricité statique est un bon moyen de séparer le code fonctionnel du code procédural/définissant l'état. Le code fonctionnel n'a généralement besoin d'aucune extension et ne change que lorsqu'il y a des bogues.
Il y a aussi l'utilisation de statique comme mécanisme de contrôle d'accès - comme avec les singletons.
Non, en fait, la raison de ce conseil est qu'il offre un avantage en termes de performances. Les méthodes statiques peuvent être appelées avec moins de surcharge, donc toute méthode qui n'a pas besoin d'une référence à this
doit être rendue statique.
Non, il n'y a pas d'inconvénients, mais lorsque vous n'accédez à aucun membre d'instance dans la méthode, il n'y a aucun sens à l'avoir comme méthode d'instance. C'est une bonne compétence de programmation de l'avoir comme méthode statique.
et en ajoutant que vous n'avez pas à créer d'instances pour accéder à ces méthodes et ainsi économiser de la mémoire et du temps de récupération de place.
Il ne devrait pas y avoir d'inconvénients - il peut même y avoir un léger avantage en termes de performances (bien qu'il ne soit pas mesurable) car la recherche dynamique peut être évitée.
Il est agréable de marquer les fonctions en tant que fonctions au lieu de les faire ressembler à des méthodes - (et les fonctions "Méthodes" SONT statiques, pas des méthodes - c'est en fait par définition).
En général, une méthode statique est une mauvaise odeur de code OO - cela signifie probablement que votre modèle OO n'est pas entièrement intégré. Cela se produit tout le temps avec bibliothèques qui ne peuvent pas connaître le code qui l'utilisera, mais dans le code intégré non-bibliothèque, les méthodes statiques doivent être examinées pour évaluer les paramètres auxquels il est le plus étroitement associé - il y a de fortes chances qu'il soit membre de cette classe.
Si une méthode statique ne prend que des valeurs natives, il vous manque probablement une poignée de classes; vous devez également limiter au minimum les variables natives ou les objets de bibliothèque (comme les collections) - au lieu de les contenir dans des classes avec une logique métier.
Je suppose que ce que je dis, c'est que si c'est vraiment un problème, vous voudrez peut-être réexaminer vos pratiques de modélisation - la statique devrait être si rare que ce n'est même pas un problème.
Un inconvénient est que vos méthodes statiques sont générales et réparties dans différentes classes en ce qui concerne l'utilisation. Vous pourriez envisager de mettre toutes les méthodes statiques qui sont générales dans une classe utilitaire.
Comme d'autres l'ont dit, il offre un léger avantage en termes de performances et constitue une bonne pratique de programmation. La seule exception est lorsque la méthode doit être une méthode d'instance à des fins de substitution, mais celles-ci sont généralement facilement reconnaissables. Par exemple, si une classe fournit le comportement par défaut d'une méthode d'instance, cela ne nécessite pas de variables d'instance, qui ne peuvent clairement pas être rendues statiques.
En général:
Vous devez écrire votre logiciel pour profiter des interfaces et non des implémentations. Qui peut dire que "maintenant", vous n'utiliserez pas de variable d'instance, mais à l'avenir, vous le ferez? Un exemple de codage vers les interfaces ...
ArrayList badList = new ArrayList(); //bad
List goodList = new ArrayList(); //good
Vous devriez être autorisé à échanger les implémentations, en particulier pour les simulations et les tests. L'injection de dépendances Spring est plutôt sympa à cet égard. Injectez simplement l'implémentation de Spring et de bingo, vous avez à peu près une méthode "statique" (enfin, singleton) ...
Maintenant, ces types d'API qui ont un but purement "utilitaire" (c'est-à-dire Apache Commons Lang) sont l'exception ici parce que je pense que la plupart (sinon la totalité) des implémentations sont statiques. Dans cette situation, quelles sont les chances que vous souhaitiez échanger Apache Commons contre une autre API?
Plus précisément:
Comment géreriez-vous avec élégance la "statique" de votre implémentation lorsque vous ciblez, disons, un déploiement Websphere contre Tomcat? Je suis sûr qu'il y aurait un cas (sans jeu de mots) où votre implémentation différerait entre les deux ... et s'appuyer sur une méthode statique dans l'une de ces implémentations spécifiques pourrait être dangereux ...