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 arguments que j'avais pour devenir statique étaient:
Les arguments pour l'usine en tant que singleton étaient les suivants:
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.
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:
Désavantages:
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.
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:
StringUtils
est correctement conçu et implémenté?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?