In den letzten Monaten bin ich ein paar Mal über die folgende Technik / das folgende Muster gestolpert. Ich kann jedoch scheinbar keinen bestimmten Namen finden und bin mir auch nicht hundertprozentig sicher, welche Vor- und Nachteile er hat.
Das Muster lautet wie folgt:
Innerhalb einer Java-Schnittstelle wird wie gewohnt eine Reihe gängiger Methoden definiert. Bei Verwendung einer inneren Klasse geht jedoch eine Standardinstanz über die Schnittstelle verloren.
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
}
}
}
Für mich liegt der größte Vorteil darin, dass ein Entwickler nur über die Oberfläche und nicht über deren Implementierungen Bescheid wissen muss, z. B. für den Fall, dass er schnell eine Instanz erstellen möchte.
Vehicle someVehicle = Vehicle.Default.getInstance();
someVehicle.accelerate();
Außerdem habe ich gesehen, dass diese Technik zusammen mit Spring verwendet wird, um Instanzen abhängig von der Konfiguration dynamisch bereitzustellen. In dieser Hinsicht kann dies auch bei der Modularisierung hilfreich sein.
Trotzdem kann ich das Gefühl nicht loswerden, dass dies ein Missbrauch der Schnittstelle ist, da sie die Schnittstelle mit einer ihrer Implementierungen koppelt. (Prinzip der Abhängigkeitsinversion usw.) Kann mir jemand erklären, wie diese Technik heißt und welche Vor- und Nachteile sie hat?
Aktualisieren:
Nach einiger Zeit zum Nachdenken überprüfte ich erneut und stellte fest, dass die folgende Singleton-Version des Musters weitaus häufiger verwendet wurde. In dieser Version wird eine öffentliche statische Instanz über die Schnittstelle verfügbar gemacht, die nur einmal initialisiert wird (da das Feld endgültig ist). Außerdem wird die Instanz fast immer mit Spring oder einer generischen Factory abgerufen, die die Schnittstelle von der Implementierung entkoppelt.
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;
Kurz gesagt: Es scheint, dass dies ein benutzerdefiniertes Singleton- / Factory-Muster ist, mit dem im Grunde genommen eine Instanz oder ein Singleton über ihre Schnittstelle verfügbar gemacht werden kann. In Bezug auf die Nachteile wurden einige in den nachstehenden Antworten und Kommentaren genannt. Bisher scheint der Vorteil in seiner Bequemlichkeit zu liegen.
quelle
Vehicle.Default
als Factory-Klasse in den Package-Namespace hochgeschoben werden, zVehicleFactory
.Vehicle.Default.getInstance() != Vehicle.Default.getInstance()
Antworten:
Default.getInstance
IMHO ist nur eine sehr spezifische Form einer Factory-Methode , die mit einer Namenskonvention aus dem Singleton-Muster verwechselt wird (ohne jedoch eine Implementierung des letzteren zu sein). In der jetzigen Form ist dies eine Verletzung des " Single-Responsibility-Prinzips ", da die Schnittstelle (die bereits zum Deklarieren einer Klassen-API dient) die zusätzliche Verantwortung für die Bereitstellung einer Standardinstanz übernimmt, die in einer separaten Instanz viel besser platziert wäreVehicleFactory
Klasse.Das Hauptproblem, das durch dieses Konstrukt verursacht wird, ist, dass es eine zyklische Abhängigkeit zwischen
Vehicle
und induziertCar
. In der aktuellen Form ist es beispielsweise nicht möglich,Vehicle
in einer Bibliothek undCar
in einer anderen zu platzieren. Die Verwendung einer separaten Factory-Klasse und die Platzierung derDefault.getInstance
Methode dort würde das Problem lösen. Es kann auch eine gute Idee sein, dieser Methode einen anderen Namen zu geben, um Verwirrung in Bezug auf Singletons zu vermeiden. Das Ergebnis könnte also so etwas wie seinVehicleFactory.createDefaultInstance()
quelle
VehicleFactory
konventioneller als das verschachtelte KonstruktVehicle.Default
, insbesondere mit der irreführenden Methode "getInstance". Obwohl es möglich ist, die zyklische Abhängigkeit mit Spring zu umgehen, würde ich persönlich die erste Variante aus Gründen des gesunden Menschenverstands vorziehen. Und dennoch haben Sie die Möglichkeit, das Werk auf eine andere Komponente zu verlagern und anschließend die Implementierung so zu ändern, dass sie ohne Feder usw. funktioniert.autodrive()
Methode hinzufügt . Ihr sehr generisches Auto muss sich dann ändern, um diese Methode zu implementieren, z. B. durch Auslösen einer NotImplementedException, aber dies liefert keinen zusätzlichen Grund für eine Änderung des Fahrzeugs.Das sieht für mich wie ein Null-Objektmuster aus .
Aber das hängt stark davon ab, wie die
Car
Klasse implementiert ist. Wenn es auf "neutrale" Weise implementiert wird, ist es definitiv ein Null-Objekt. Das heißt, wenn Sie alle Nullprüfungen auf der Schnittstelle entfernen und die Instanz dieser Klasse anstelle jedes Vorkommens von setzen könnennull
, muss der Code weiterhin ordnungsgemäß funktionieren.Auch wenn es sich um dieses Muster handelt, besteht kein Problem darin, eine enge Kopplung zwischen der Schnittstelle selbst und der Implementierung des Nullobjekts herzustellen. Denn Null-Objekt kann überall sein, wo diese Schnittstelle verwendet wird.
quelle