Il y a quelques mois, j'ai commencé à travailler sur un nouveau projet, et lorsque je parcourais le code, il me frappait la quantité de méthodes statiques utilisées. Non seulement les méthodes utilitaires en tant que collectionToCsvString(Collection<E> elements)
, mais aussi beaucoup de logique métier y sont conservées.
Quand j'ai demandé au gars responsable de la raison derrière cela, il a dit que c'était un moyen d' échapper à la tyrannie de Spring . Cela va quelque chose autour de ce processus de réflexion: pour mettre en œuvre une méthode de création de reçu client, nous pourrions avoir un service
@Service
public class CustomerReceiptCreationService {
public CustomerReceipt createReceipt(Object... args) {
CustomerReceipt receipt = new CustomerReceipt();
// creation logic
return receipt;
}
}
Maintenant, le gars a dit qu'il déteste avoir des classes gérées inutilement par Spring, essentiellement parce qu'il impose la restriction que les classes clientes doivent être elles-mêmes des beans Spring. Nous finissons par tout gérer par Spring, ce qui nous oblige à peu près à travailler avec des objets sans état d'une manière procédurale. Plus ou moins ce qui est indiqué ici https://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html
Donc, au lieu du code ci-dessus, il a
public class CustomerReceiptCreator {
public static CustomerReceipt createReceipt(Object... args) {
CustomerReceipt receipt = new CustomerReceipt();
// creation logic
return receipt;
}
}
Je pourrais argumenter au point d'éviter que Spring gère nos cours lorsque cela est possible, mais ce que je ne vois pas, c'est l'avantage d'avoir tout statique. Ces méthodes statiques sont également sans état, donc pas très OO. Je me sentirais plus à l'aise avec quelque chose
new CustomerReceiptCreator().createReceipt()
Il affirme que les méthodes statiques ont des avantages supplémentaires. À savoir:
- Plus facile à lire. Importez la méthode statique et nous n'avons qu'à nous soucier de l'action, pas de la classe qui la fait.
- Est évidemment une méthode sans appels DB, donc bon marché en termes de performances; et c'est une bonne chose de le préciser, de sorte que le client potentiel doive entrer dans le code et vérifier cela.
- Écriture de tests plus facile.
Mais j'ai juste l'impression qu'il y a quelque chose qui ne va pas complètement dans tout ça, donc j'aimerais entendre les réflexions de développeurs plus expérimentés à ce sujet.
Donc ma question est, quels sont les pièges potentiels de cette façon de programmer?
la source
static
méthode que vous illustrez ci-dessus n'est qu'une méthode d'usine ordinaire. Rendre les méthodes d'usine statiques est la convention généralement acceptée, pour un certain nombre de raisons impérieuses. La question de savoir si la méthode d'usine est appropriée ici est différente.Réponses:
Quelle est la différence entre
new CustomerReceiptCreator().createReceipt()
etCustomerReceiptCreator.createReceipt()
? À peu près aucun. La seule différence significative est que le premier cas a une syntaxe considérablement plus délicate. Si vous suivez le premier dans la croyance qu'en évitant d'une manière ou d'une autre les méthodes statiques améliore votre code OO, vous vous trompez gravement. Créer un objet juste pour appeler une seule méthode dessus est une méthode statique par syntaxe obtuse.Là où les choses deviennent différentes, c'est lorsque vous injectez
CustomerReceiptCreator
au lieu de lesnew
ingérer. Prenons un exemple:Comparons ceci une version de méthode statique:
L'avantage de la version statique est que je peux facilement vous dire comment elle interagit avec le reste du système. Ce n'est pas le cas. Si je n'ai pas utilisé de variables globales, je sais que le reste du système n'a pas été modifié d'une manière ou d'une autre. Je sais qu'aucune autre partie du système ne peut affecter la réception ici. Si j'ai utilisé des objets immuables, je sais que l'ordre n'a pas changé et le createReceipt est une fonction pure. Dans ce cas, je peux librement déplacer / supprimer / etc cet appel sans me soucier des effets aléatoires imprévisibles ailleurs.
Je ne peux pas faire les mêmes garanties si j'ai injecté le
CustomerReceiptCreator
. Il peut avoir un état interne qui est modifié par l'appel, je peux être affecté par ou changer un autre état. Il peut y avoir des relations imprévisibles entre les instructions dans ma fonction, de sorte que changer l'ordre introduira des bogues surprenants.D'un autre côté, que se passe-t-il si
CustomerReceiptCreator
soudain a besoin d'une nouvelle dépendance? Disons qu'il doit vérifier un indicateur de fonctionnalité. Si nous injections, nous pourrions faire quelque chose comme:Ensuite, nous avons terminé, car le code appelant sera injecté avec un
CustomerReceiptCreator
qui sera automatiquement injecté unFeatureFlags
.Et si nous utilisions une méthode statique?
Mais attendez! le code appelant doit également être mis à jour:
Bien sûr, cela laisse encore la question de savoir d'où
processOrder
vient son origineFeatureFlags
. Si nous sommes chanceux, la piste se termine ici, sinon, la nécessité de passer par FeatureFlags est poussée plus loin dans la pile.Il y a un compromis ici. Méthodes statiques nécessitant de passer explicitement autour des dépendances, ce qui entraîne plus de travail. La méthode injectée réduit le travail, mais rend les dépendances implicites et donc cachées, rendant le code plus difficile à raisonner.
la source