web-dev-qa-db-fra.com

Usine statique vs usine en tant que singleton

Dans certains de mes codes, j'ai une fabrique statique similaire à celle-ci:

public class SomeFactory {

    // Static class
    private SomeFactory() {...}

    public static Foo createFoo() {...}

    public static Foo createFooerFoo() {...}
}

Lors d'une révision du code, il a été proposé que ce soit un singleton et injecté. Donc, cela devrait ressembler à ceci:

public class SomeFactory {

    public SomeFactory() {}

    public Foo createFoo() {...}

    public Foo createFooerFoo() {...}
}

Quelques points à souligner:

  • Les deux usines sont apatrides.
  • La seule différence entre les méthodes est leur portée (instance vs statique). Les implémentations sont les mêmes.
  • Foo est un bean qui n'a pas d'interface.

Les arguments que j'avais pour devenir statique étaient:

  • La classe est sans état, elle n'a donc pas besoin d'être instanciée
  • Il semble plus naturel de pouvoir appeler une méthode statique que d'avoir à instancier une fabrique

Les arguments pour l'usine en tant que singleton étaient les suivants:

  • C'est bon de tout injecter
  • Malgré l'apatridie de l'usine, le test est plus facile avec l'injection (facile à se moquer)
  • Il faut se moquer lors du test du consommateur

J'ai de sérieux problèmes avec l'approche singleton car elle semble suggérer qu'aucune méthode ne devrait jamais être statique. Il semble également suggérer que des utilitaires tels que StringUtils devraient être enveloppés et injectés, ce qui semble idiot. Enfin, cela implique que je devrai se moquer de l'usine à un moment donné, ce qui ne semble pas correct. Je ne peux pas penser au moment où je devrais me moquer de l'usine.

Que pense la communauté? Bien que je n'aime pas l'approche singleton, je ne semble pas avoir d'argument terriblement fort contre.

25
bstempi

Pourquoi sépareriez-vous votre usine du type d'objet qu'elle crée?

public class Foo {

    // Private constructor
    private Foo(...) {...}

    public static Foo of(...) { return new Foo(...); }
}

C'est ce que Joshua Bloch décrit comme son article 1 à la page 5 de son livre, "Java efficace". Java est assez verbeux sans ajouter de classes et singletons supplémentaires et ainsi de suite. Il y a des cas où une classe d'usine séparée a du sens, mais elles sont rares et espacées.

Pour paraphraser l'élément 1 de Joshua Bloch, contrairement aux constructeurs, les méthodes d'usine statique peuvent:

  • Avoir des noms qui peuvent décrire des types spécifiques de création d'objets (Bloch utilise probablePrime (int, int, Random) comme exemple)
  • Renvoyer un objet existant (pensez: poids mouche)
  • Renvoie un sous-type de la classe d'origine ou une interface qu'il implémente
  • Réduire la verbosité (en spécifiant les paramètres de type - voir Bloch par exemple)

Désavantages:

  • Les classes sans constructeur public ou protégé ne peuvent pas être sous-classées (mais vous pouvez utiliser des constructeurs protégés et/ou le point 16: privilégier la composition à l'héritage)
  • Les méthodes de fabrique statique ne se distinguent pas des autres méthodes statiques (utilisez un nom standard comme () ou valueOf ())

Vraiment, vous devriez apporter le livre de Bloch à votre réviseur de code et voir si vous pouvez tous les deux accepter le style de Bloch.

15
GlenPeterson

Juste au-dessus de ma tête, voici quelques-uns des problèmes avec la statique en Java:

  • les méthodes statiques ne jouent pas selon les OOP "règles". Vous ne pouvez pas forcer une classe à implémenter des méthodes statiques spécifiques (pour autant que je sache). Vous ne pouvez donc pas avoir plusieurs classes implémentant le même ensemble de méthodes statiques avec les mêmes signatures. Vous êtes donc coincé avec n de tout ce que vous faites. Vous ne pouvez pas vraiment sous-classer une classe statique non plus. Donc pas de polymorphisme , etc.

  • puisque les méthodes ne sont pas des objets, les méthodes statiques ne peuvent pas être transmises et elles ne peuvent pas être utilisées (sans faire une tonne de codage standard), sauf par un code qui leur est lié de manière rigide - - ce qui rend le code client hautement couplé. (Pour être juste, ce n'est pas uniquement la faute de la statique).

  • les champs statiques sont assez similaires aux globaux


Vous avez mentionné:

J'ai de sérieux problèmes avec l'approche singleton car elle semble suggérer qu'aucune méthode ne devrait jamais être statique. Il semble également suggérer que les utilitaires tels que StringUtils devraient être enveloppés et injectés, ce qui semble idiot. Enfin, cela implique que je devrai se moquer de l'usine à un moment donné, ce qui ne semble pas correct. Je ne peux pas penser au moment où je devrais me moquer de l'usine.

Ce qui me rend curieux de vous demander:

  • quels sont, selon vous, les avantages des méthodes statiques?
  • pensez-vous que StringUtils est correctement conçu et implémenté?
  • pourquoi pensez-vous que vous n'aurez jamais besoin de vous moquer de l'usine?
5
user39685

Je pense que l'application que vous créez doit être assez simple, sinon vous êtes relativement tôt dans son cycle de vie. Le seul autre scénario auquel je peux penser où vous n'auriez pas déjà rencontré de problèmes avec votre statique est si vous avez réussi à limiter les autres parties de votre code qui connaissent votre Factory à un ou deux endroits.

Imaginez que votre application évolue et maintenant, dans certains cas, vous devez produire un FooBar (sous-classe de Foo). Maintenant, vous devez parcourir tous les endroits de votre code qui connaissent votre SomeFactory et écrire une sorte de logique qui regarde someCondition et appelle SomeFactory ou SomeOtherFactory. En fait, en regardant votre exemple de code, c'est pire que ça. Vous prévoyez d'ajouter simplement méthode après méthode pour créer différents Foos et tout votre code client doit vérifier une condition avant de déterminer quel Foo faire. Ce qui signifie que tous les clients doivent avoir une connaissance intime du fonctionnement de votre usine et qu'ils doivent tous changer chaque fois qu'un nouveau Foo est nécessaire (ou supprimé!).

Imaginez maintenant si vous venez de créer un IFooFactory qui fait un IFoo. Maintenant, produire une sous-classe différente ou même une implémentation complètement différente est un jeu d'enfant - vous alimentez simplement la bonne IFooFactory plus haut dans la chaîne, puis lorsque le client l'obtient, il appelle gimmeAFoo() et obtiendra la bonne foo parce qu'il a la bonne usine.

Vous vous demandez peut-être comment cela se joue dans le code de production. Pour vous donner un exemple de la base de code sur laquelle je travaille qui commence à se stabiliser après un peu plus d'un an de lancement de projets, j'ai un tas d'usines qui sont utilisées pour créer des objets de données Question (c'est quelque chose comme - Modèles de présentation ). J'ai un QuestionFactoryMap qui est essentiellement un hachage pour rechercher quelle usine de questions utiliser quand. Donc, pour créer une application qui pose différentes questions, j'ai mis différentes usines dans la carte.

Les questions peuvent être présentées dans des instructions ou des exercices (qui implémentent tous deux IContentBlock), de sorte que chaque InstructionsFactory ou ExerciseFactory obtiendra une copie de QuestionFactoryMap. Ensuite, les différentes usines d'instructions et d'exercices sont remises au ContentBlockBuilder qui conserve à nouveau un hachage à utiliser quand. Et puis, lorsque le XML est chargé, ContentBlockBuilder appelle la fabrique d'exercices ou d'instructions hors du hachage et le demande à createContent(), puis la fabrique délègue la construction des questions à tout ce qu'elle extrait. de son QuestionFactoryMap interne en fonction de ce qui est dans chaque nœud XML par rapport au hachage. Malheureusement , cette chose est un BaseQuestion au lieu d'un IQuestion, mais cela montre que même lorsque vous faites attention à la conception, vous pouvez faire des erreurs qui peut vous hanter sur toute la ligne.

Votre instinct vous dit que ces statiques vous blesseront, et il y a certainement beaucoup là-bas dans la littérature pour soutenir votre intestin. Vous ne perdrez jamais en ayant une injection de dépendances que vous n'utiliserez que pour vos tests unitaires (pas que je pense que si vous construisez un bon code que vous ne vous retrouverez pas à l'utiliser plus que cela), mais la statique peut complètement détruire votre capacité pour modifier rapidement et facilement votre code. Alors pourquoi même y aller?

0
Amy Blankenship