Au cours des derniers mois, je suis tombé plusieurs fois sur la technique / le schéma suivant. Cependant, je n'arrive pas à trouver un nom spécifique, et je ne suis pas sûr à 100% de tous ses avantages et inconvénients.
Le schéma se présente comme suit:
Dans une interface Java, un ensemble de méthodes communes est défini comme d'habitude. Cependant, en utilisant une classe interne, une instance par défaut est divulguée via l'interface.
public interface Vehicle {
public void accelerate();
public void decelerate();
public static class Default {
public static Vehicle getInstance() {
return new Car(); // or use Spring to retrieve an instance
}
}
}
Pour moi, il semble que le plus grand avantage réside dans le fait qu'un développeur n'a besoin que de connaître l'interface et non ses implémentations, par exemple au cas où il souhaiterait rapidement créer une instance.
Vehicle someVehicle = Vehicle.Default.getInstance();
someVehicle.accelerate();
De plus, j'ai vu cette technique utilisée avec Spring afin de fournir dynamiquement des instances en fonction de la configuration. À cet égard, il semble également que cela puisse aider à la modularisation.
Néanmoins, je ne peux pas secouer le sentiment qu'il s'agit d'une mauvaise utilisation de l'interface car elle couple l'interface avec l'une de ses implémentations. (Principe d'inversion des dépendances, etc.) Quelqu'un pourrait-il m'expliquer comment s'appelle cette technique, ainsi que ses avantages et ses inconvénients?
Mise à jour:
Après un certain temps de réflexion, j'ai revérifié et remarqué que la version singleton suivante du modèle était utilisée beaucoup plus souvent. Dans cette version, une instance statique publique est exposée via l'interface qui n'est initialisée qu'une seule fois (car le champ est final). De plus, l'instance est presque toujours récupérée à l'aide de Spring ou d'une fabrique générique qui dissocie l'interface de l'implémentation.
public interface Vehicle {
public void accelerate();
public void decelerate();
public static class Default {
public static final Vehicle INSTANCE = getInstance();
private static Vehicle getInstance() {
return new Car(); // or use Spring/factory here
}
}
}
// Which allows to retrieve a singleton instance using...
Vehicle someVehicle = Vehicle.Default.INSTANCE;
En bref: il semble qu'il s'agisse d'un modèle singleton / factory personnalisé, qui permet essentiellement d'exposer une instance ou un singleton via son interface. En ce qui concerne les inconvénients, quelques-uns ont été nommés dans les réponses et commentaires ci-dessous. Jusqu'à présent, l'avantage semble résider dans sa commodité.
Vehicle.Default
être déplacé vers le haut dans l'espace de noms du package en tant que classe d'usine, par exempleVehicleFactory
.Vehicle.Default.getInstance() != Vehicle.Default.getInstance()
Réponses:
Default.getInstance
est à mon humble avis juste une forme très spécifique d'une méthode d'usine , mélangée à une convention de dénomination tirée du modèle singleton (mais sans être une implémentation de ce dernier). Dans la forme actuelle, il s'agit d'une violation du " principe de responsabilité unique ", car l'interface (qui sert déjà à déclarer une API de classe) prend la responsabilité supplémentaire de fournir une instance par défaut, qui serait beaucoup mieux placée dans une autreVehicleFactory
classe.Le principal problème causé par cette construction est qu'elle induit une dépendance cyclique entre
Vehicle
etCar
. Par exemple, dans le formulaire actuel, il ne sera pas possible de placerVehicle
dans une bibliothèque etCar
dans une autre. Utiliser une classe d'usine distincte et y placer laDefault.getInstance
méthode résoudrait ce problème. Il peut également être judicieux de donner un nom différent à cette méthode pour éviter toute confusion liée aux singletons. Le résultat pourrait donc être quelque chose commeVehicleFactory.createDefaultInstance()
la source
VehicleFactory
est plus conventionnelle que la construction imbriquéeVehicle.Default
, en particulier avec la méthode trompeuse "getInstance". Bien qu'il soit possible de contourner la dépendance cyclique en utilisant Spring, je préférerais personnellement la première variante juste pour le bon sens. Et, cela vous laisse toujours la possibilité de déplacer l'usine vers un autre composant, de changer ensuite l'implémentation pour qu'elle fonctionne sans Spring etc.autodrive()
méthode. Votre voiture très générique doit alors changer pour implémenter cette méthode, par exemple en lançant une exception NotImplementedException, mais cela ne confère pas de raison supplémentaire de changer sur le véhicule.Cela ressemble à un motif d' objet nul pour moi.
Mais cela dépend fortement de la façon dont la
Car
classe est implémentée. S'il est implémenté de manière "neutre", alors c'est définitivement un objet nul. Cela signifie que si vous pouvez supprimer toutes les vérifications nulles sur l'interface et mettre l'instance de cette classe au lieu de chaque occurrence denull
, le code doit toujours fonctionner correctement.De plus, s'il s'agit de ce modèle, il n'y a aucun problème à créer un couplage étroit entre l'interface elle-même et la mise en œuvre d'un objet nul. Parce que l'objet nul peut être partout où cette interface est utilisée.
la source