Choisissez une implémentation concrète lors de l’exécution avec Java 8

Je ne sais pas trop où placer le switch / if lors du choix de l’implémentation / sous-classe à instancier, en particulier si l’on considère que les interfaces peuvent désormais avoir des méthodes statiques.

Disons que j’ai un service, un type défini par une interface et quelques implémentations. Je suppose qu’il serait préférable de ne pas mettre cette logique dans le service, mais d’avoir la méthode de l’usine. Mais devrait-il aller dans l’interface ou dans une autre classe avec une carte param-to-type, comme suggéré dans cette réponse ?

Il me semble naturel de le mettre dans l’interface:

 public interface MyInterface { public void doSomething(); public static MyInterface create(int param) { if (param == 0) return new ImplA(); else return new ImplB(); } } 

et ensuite simplement, appelez-le depuis le service:

 public class MyService { public void serveMe(int param) { MyInterface.create(param).doSomething(); } } 

Mais je ne sais pas s’il est mauvais que l’interface sache à propos de son implémentation, ou qu’une classe parent connaisse ses sous-types. Alors

  1. Où devrais-je mettre cette logique?
  2. Cela changerait-il beaucoup si je choisis des sous-classes d’un type?

Utilisez Factory pour cela. De cette façon, vous pourrez maintenir le single responsibility principle . Cependant, dans l’un de mes propres projets, l’ interface définit une méthode qui détermine le type d’argument à utiliser. Grâce à cela et à l’aide de Reflections , tout le processus est automatisé. Reflections trouve toutes les classes qui implémentent une interface donnée et stocke son “type d’utilisation” dans une carte pour une recherche rapide. Grâce à cette solution, tout ce que le développeur doit faire s’il a besoin d’une nouvelle implémentation, c’est la créer. Aucune autre modification n’est requirejse dans d’autres parties du système, pas même en classe d’usine.

Reflections a une fonctionnalité intéressante pour stocker les métadonnées au moment de la compilation, aussi la recherche à l’exécution pour les classes appropriées est-elle un clin d’œil

Il existe de nombreuses solutions à votre problème, je pense que vous en connaissez déjà beaucoup.

Le modèle de méthode d’usine statique

 interface Interface { public static Interface create(...); } 

Cela fonctionne, mais rend difficile la séparation de l’interface et de la mise en œuvre. L’interface doit connaître toutes les implémentations possibles et n’est pas particulièrement extensible. Pour append une nouvelle implémentation, vous devez modifier l’interface.

Modèle d’usine

C’est plus ou moins old-school du livre GOF:

 interface Factory { public Interface create(...) } 

Cela vous permet de remplacer l’usine (et même les usines de la stack). Cependant, il a l’inconvénient de devoir contourner l’object usine. Notez qu’avec Java 8, vous pouvez également utiliser des fabriques très légères basées sur lambda (voir https://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html ).

Les conteneurs

Une autre solution, même si elle peut être assez lourde, consiste à laisser la construction de l’object à un conteneur. Dans ces cas, le conteneur fournit votre fabrique d’objects. Le type d’object est laissé à la configuration. Spring et Java EE font tout un travail dans ce domaine. Combinez également avec l’ dependency injection pour un effet supplémentaire.

Ce sont au moins ceux que je peux penser.