En los últimos meses, me topé algunas veces con la siguiente técnica / patrón. Sin embargo, parece que no puedo encontrar un nombre específico, ni estoy 100% seguro de todas sus ventajas y desventajas.
El patrón es el siguiente:
Dentro de una interfaz Java, un conjunto de métodos comunes se define como de costumbre. Sin embargo, al usar una clase interna, se filtra una instancia predeterminada a través de la interfaz.
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
}
}
}
Para mí, parece que la mayor ventaja radica en el hecho de que un desarrollador solo necesita saber acerca de la interfaz y no de sus implementaciones, por ejemplo, en caso de que quiera crear rápidamente una instancia.
Vehicle someVehicle = Vehicle.Default.getInstance();
someVehicle.accelerate();
Además, he visto que esta técnica se usa junto con Spring para proporcionar dinámicamente instancias dependiendo de la configuración. En este sentido, también parece que esto puede ayudar con la modularización.
Sin embargo, no puedo evitar la sensación de que se trata de un mal uso de la interfaz, ya que combina la interfaz con una de sus implementaciones. (Principio de inversión de dependencia, etc.) ¿Podría alguien explicarme cómo se llama esta técnica, así como sus ventajas y desventajas?
Actualizar:
Después de un tiempo de consideración, volví a comprobar y noté que la siguiente versión singleton del patrón se usaba con mucha más frecuencia. En esta versión, una instancia estática pública se expone a través de la interfaz que se inicializa solo una vez (debido a que el campo es final). Además, la instancia casi siempre se recupera utilizando Spring o una fábrica genérica que desacopla la interfaz de la implementación.
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 pocas palabras: parece que este es un patrón singleton / factory personalizado, que básicamente permite exponer una instancia o un singleton a través de su interfaz. Con respecto a las desventajas, se han mencionado algunas en las respuestas y comentarios a continuación. Hasta ahora, la ventaja parece radicar en su conveniencia.
fuente
Vehicle.Default
debería moverse hacia arriba en el espacio de nombres del paquete como una clase de fábrica, por ejemploVehicleFactory
.Vehicle.Default.getInstance() != Vehicle.Default.getInstance()
Respuestas:
Default.getInstance
En mi humilde opinión, es solo una forma muy específica de un método de fábrica , mezclado con una convención de nomenclatura tomada del patrón singleton (pero sin ser una implementación de este último). En la forma actual, esto es una violación del " principio de responsabilidad única ", porque la interfaz (que ya cumple el propósito de declarar una API de clase) asume la responsabilidad adicional de proporcionar una instancia predeterminada, que sería mucho mejor ubicar en un sitio separadoVehicleFactory
clase.El principal problema causado por esta construcción es que induce una dependencia cíclica entre
Vehicle
yCar
. Por ejemplo, en la forma actual no será posible colocarloVehicle
en una biblioteca yCar
en otra. Usar una clase de fábrica separada y colocar elDefault.getInstance
método allí resolvería ese problema. También puede ser una buena idea darle a ese método un nombre diferente, para evitar cualquier confusión relacionada con los singletons. Entonces el resultado podría ser algo comoVehicleFactory.createDefaultInstance()
fuente
VehicleFactory
es más convencional que la construcción anidadaVehicle.Default
, especialmente con el método engañoso "getInstance". Aunque es posible eludir la dependencia cíclica utilizando Spring, personalmente preferiría la primera variante solo por el sentido común. Y, aún así, le deja la opción de trasladar la fábrica a un componente diferente, luego cambiar la implementación para que funcione sin Spring, etc.autodrive()
método. Su automóvil muy genérico debe cambiar para implementar ese método, por ejemplo, lanzando una excepción NotImplementedException, pero eso no confiere una razón adicional para cambiar en el vehículo.Esto me parece un patrón de objeto nulo .
Pero eso depende en gran medida de cómo
Car
se implemente la clase. Si se implementa de forma "neutral", definitivamente es un objeto nulo. Eso significa que si puede eliminar todas las comprobaciones nulas en la interfaz y colocar la instancia de esta clase en lugar de cada aparición denull
, entonces el código aún debe funcionar correctamente.Además, si se trata de este patrón, no hay problema de crear un acoplamiento estrecho entre la interfaz en sí y la implementación del objeto nulo. Porque el objeto nulo puede estar en todas partes donde se usa dicha interfaz.
fuente