web-dev-qa-db-fra.com

Quelle est la meilleure pratique pour créer des classes Stateless Utility en Java

Quelle est la meilleure pratique pour créer les classes Utility (qui ne contiennent aucun état) en Java? 

Dans la plupart des cas, nous finissons par créer des méthodes statiques pour de telles tâches. Une autre manière possible pourrait être de "créer les objets singleton" pour effectuer cette opération.

Quelle devrait être la considération de conception lorsque l'exigence est que le code devrait être facilement vérifiable par unité?

11
Yogesh

Si vous cédez un peu ma métaphore…

Vous avez probablement déjà vu un de ces derniers:

Device

Notez que nous appelons cela un grille-pain. Nous appelons not un "BreadUtil".

De même, les méthodes utilitaires peuvent et doivent être placées dans une classe nommée pour des fonctionnalités spécifiques, et non pas «divers éléments liés au pain».

La plupart du temps, vos méthodes statiques appartiennent à une classe liée. Par exemple, Integer.parseInt est une méthode statique de la classe Integer et non un membre d'une classe théorique IntegerUtil ou NumberUtil.

Dans le passé, la création d'une classe d'utilitaires distinct était l'un des cas où la classe d'intérêt principale était une interface. Un exemple de ceci est Java.util.Collections . Cependant, à partir de Java 8, ce n'est pas une excuse, car les interfaces peuvent avoir des méthodes statiques et des méthodes par défaut. En fait, Collections.sort (List) a déjà été migré vers List.sort .

Si vous avez beaucoup de méthodes d’utilité et que vous pensez qu’elles encombreraient la classe correspondante, vous pouvez les placer dans une classe séparée, mais pas dans la classe «BreadUtil». Il n'est jamais acceptable de mettre le mot "util" dans un nom de classe (ou "utils", "utilitaires", "misc", "divers", "général", "partagé", "commun" ou "cadre") . Donnez à la classe un nom explicite décrivant l’utilisation des méthodes. Si les méthodes sont trop diverses pour permettre un tel nom de classe, vous devrez probablement les scinder en plusieurs classes. (De petites classes avec seulement quelques méthodes sont parfaitement acceptables; beaucoup de gens considèrent même cela comme un bon design.)

Pour revenir à l'exemple Integer, si vous estimez que les méthodes encombrent la classe, vous pouvez créer de nouvelles classes comme ceci:

public class IntegerMath {
    private IntegerMath() { }

    public static int compare(int x, int y) { /* ... */ }
    public static int compareUnsigned(int x, int y) { /* ... */ }
    public static int divideUnsigned(int dividend, int divisor) { /* ... */ }
    public static int min(int a, int b) { /* ... */ }
    public static int max(int a, int b) { /* ... */ }
    public static int remainderUnsigned(int dividend, int divisor) { /* ... */ }
    public static int signum(int i) { /* ... */ }
    public static int sum(int a, int b) { /* ... */ }
    public static long toUnsignedLong(int i) { /* ... */ }
}

public class IntegerBits {
    private IntegerBits() { }

    public static int bitCount(int i) { /* ... */ }
    public static int highestOneBit(int i) { /* ... */ }
    public static int lowestOneBit(int i) { /* ... */ }
    public static int numberOfLeadingZeros(int i) { /* ... */ }
    public static int numberOfTrailingZeros(int i) { /* ... */ }
    public static int reverse(int i) { /* ... */ }
    public static int reverseBytes(int i) { /* ... */ }
    public static int rotateLeft(int i, int distance) { /* ... */ }
    public static int rotateRight(int i, int distance) { /* ... */ }
}

public class IntegerParser {
    private IntegerParser() { }

    public static int parseInt(String s) { /* ... */ }
    public static int parseInt(String s, int radix) { /* ... */ }
    public static int parseUnsignedInt(String s) { /* ... */ }
    public static int parseUnsignedInt(String s, int radix) { /* ... */ }
}

Le dernier de ceux-ci est un exemple de quelque chose qui pourrait être mieux sans méthodes statiques:

public class IntegerParser {
    public IntegerParser() { this(10); }
    public IntegerParser(int radix) { /* ... */ }

    public int parseInt(String s) { /* ... */ }
    public int parseUnsignedInt(String s) { /* ... */ }
}
22
VGR

Je pense que le moyen le plus courant consiste à créer des méthodes statiques. Par exemple, voir StringUtils dans Apache Commons Lang , Strings in Guava ou même Tableaux dans JDK .

De plus, la classe devrait être finalisée et avoir un constructeur privé pour éviter de l'hériter ou de l'instancier.

Que vous utilisiez des méthodes statiques ou un singleton, le test unitaire devrait être le même. Dans ce dernier cas, vous pourriez écrire un peu plus de code (caractères).

Je sais que OO les puristes soutiendront l’existence même de telles classes et j’aurai tendance à être d’accord avec elles, mais elles ne sont ajoutées que par souci de simplicité et vous devez limiter le nombre de ces classes.

4
Adrian Ber

Si vous utilisez des frameworks tels que Spring, vous pouvez créer une classe utilitaire avec l'annotation @Service ..__, ce qui garantira son instace unique (SIngleton) et un moyen simple de l'injecter dans n'importe quelle autre classe dont les méthodes sont nécessaires. .

Dans tous les autres cas, je suggèrerais de faire un singleton, en utilisant un motif d'usine, ou en utilisant une méthode statique, en utilisant uniquement des méthodes statiques.

2
theDima

Static est un singleton. Une instance singleton avec des méthodes non statiques n’est nécessaire que lorsque vous avez besoin de plusieurs variantes de classe d’utilitaire avec différentes valeurs de propriété/paramètre (s) à utiliser. Comme exemple lorsque votre utilitaire a une propriété (par exemple, customProperty) et que vous avez besoin de deux cas différents en même temps. 

  1. lorsque l'utilitaire doit utiliser customProperty = valeur1

  2. lorsque Utitlity doit utiliser customProperty = valeur2 ... 

mais c'est assez étrange, maladroit et pas bon ... Un appelant utilitaire peut fournir la valeur de propriété nécessaire dans la méthode statique . Donc, ne vous en tenez pas à cela. Faites en sorte que les méthodes Utility soient toujours statiques et ne vous souciez pas des schémas "théoriques" ... :)

0
Vadim

Quelle est la meilleure pratique pour créer les classes Utility (qui ne contiennent aucun état) en Java?.

À mon avis, le meilleur moyen est d’omettre les classes d’utilité autant que possible.

Une classe d'utilitaires inverse l'idée de programmation orientée objet. Lorsque vous arrivez au point que vous avez besoin d’une nouvelle méthode, vous l’ajoutez généralement à la classe avec la plus grande cohésion. Cela signifie pour la classe qui contient la plupart des informations nécessaires à la méthode. Les autres informations seront transmises à la méthode en tant que paramètres. Si la liste de paramètres devient trop longue, c'est souvent un indicateur pour une méthode mal placée.

Il existe de rares situations où vous avez vraiment besoin d'une classe d'utilitaires pour fournir une méthode pour un type. Par exemple. 

  • si vous ne pouvez pas ajouter la méthode au code source, car vous ne la possédez pas (mais vous pouvez créer un wrapper ou le sous-classer)
  • si une classe qui a besoin de la méthode supplémentaire est finale et que vous ne possédez pas le code source (par exemple, StringUtility)

Dans la plupart des cas, nous finissons par créer des méthodes statiques pour de telles tâches. Une autre méthode possible pourrait être "créer les objets singleton" pour effectuer cette opération.

Quelle devrait être la considération de conception lorsque l'exigence est que le code devrait être facilement vérifiable par unité?

Si vous examinez les classes d'utilitaires du point de vue de la testabilité d'unité et que vous souhaitez pouvoir vous moquer de la classe utiltiy, vous devez utiliser le singleton, car les références d'objet peuvent être remplacées. 

Soit en modifiant la référence à l'instance singleton (la variable statique), soit du côté client en utilisant un champ d'objet. Par exemple.

 private SomeUtility someUtility = SomeUtiltiy.INSTANCE;

 public void someMethod(...){
    // Can be replaced by changing the someUtility reference
    someUtility.doSomething(....); 

    // A static call like 
    // SomeUtility.doSomething(...);
    // can not be easily replaced.
 }

Les appels de méthodes statiques sont difficiles à remplacer. Certains frameworks de test tels que powermock support mocking d'appels statiques en réécrivant le bytecode du client. Mais je pense que ces frameworks ont été conçus pour prendre en charge le test unitaire du mauvais code hérité. Si vous avez besoin de powermock pour le nouveau code, vous devez repenser votre conception.

0
René Link