java - gui - Clases de fábrica
java swing tutorial (7)
Personalmente, nunca he entendido la idea de las clases de fábrica porque me parece mucho más útil simplemente crear una instancia de un Objeto directamente. Mi pregunta es simple, ¿en qué situación es el uso de un patrón de clase de fábrica la mejor opción, por qué motivo, y cómo se ve una buena clase de fábrica?
Aquí hay una fábrica real en vivo de mi código base. Se utiliza para generar una clase de muestra que sabe cómo muestrear datos de algún conjunto de datos (originalmente está en C #, así que disculpe cualquier java faux-pas)
class SamplerFactory
{
private static Hashtable<SamplingType, ISampler> samplers;
static
{
samplers = new Hashtable<SamplingType, ISampler>();
samplers.put(SamplingType.Scalar, new ScalarSampler());
samplers.put(SamplingType.Vector, new VectorSampler());
samplers.put(SamplingType.Array, new ArraySampler());
}
public static ISampler GetSampler(SamplingType samplingType)
{
if (!samplers.containsKey(samplingType))
throw new IllegalArgumentException("Invalid sampling type or sampler not initialized");
return samplers.get(samplingType);
}
}
y aquí hay un ejemplo de uso:
ISampler sampler = SamplerFactory.GetSampler(SamplingType.Array);
dataSet = sampler.Sample(dataSet);
Como ve, no es mucho código, e incluso podría ser más corto y más rápido solo para hacer
ArraySampler sampler = new ArraySampler();
dataSet = sampler.Sample(dataSet);
que usar la fábrica Entonces, ¿por qué siquiera me molesto? Bueno, hay dos razones básicas, que se complementan entre sí:
En primer lugar, es la simplicidad y la facilidad de mantenimiento del código. Digamos que en el código de llamada, la
enum
se proporciona como un parámetro. Es decir, si tuviera un método que necesita procesar los datos, incluido el muestreo, puedo escribir:void ProcessData(Object dataSet, SamplingType sampling) { //do something with data ISampler sampler = SamplerFactory.GetSampler(sampling); dataSet= sampler.Sample(dataSet); //do something other with data }
en lugar de una construcción más engorrosa, como esta:
void ProcessData(Object dataSet, SamplingType sampling) { //do something with data ISampler sampler; switch (sampling) { case SamplingType.Scalar: sampler= new ScalarSampler(); break; case SamplingType.Vector: sampler= new VectorSampler(); break; case SamplingType.Array: sampler= new ArraySampler(); break; default: throw new IllegalArgumentException("Invalid sampling type"); } dataSet= sampler.Sample(dataSet); //do something other with data }
Tenga en cuenta que esta monstruosidad debe escribirse cada vez que necesito un poco de muestra. Y pueden imaginarse lo divertido que sería cambiar si, digamos, agregué un parámetro al constructor de
ScalarSampler
o agregué un nuevoSamplingType
. Y esta fábrica solo tiene tres opciones ahora, imagine una fábrica con 20 implementaciones.En segundo lugar, es el desacoplamiento del código. Cuando uso una fábrica, el código de llamada no sabe o no necesita saber que existe una clase llamada
ArraySampler
. La clase podría incluso resolverse en tiempo de ejecución, y el sitio de llamadas no sería más inteligente. Por lo tanto, en consecuencia, puedo cambiar la claseArraySampler
todo lo que quiera, incluida, entre otras, la eliminación directa de la clase, por ejemplo, si decido que elScalarSampler
debe usar para los datos de la matriz. Solo necesitaría cambiar la líneasamplers.put(SamplingType.Array, new ArraySampler());
a
samplers.put(SamplingType.Array, new ScalarSampler());
y funcionaría mágicamente No tengo que cambiar una sola línea de código en las clases de llamadas, que podrían ser cientos. Efectivamente, la fábrica me permite controlar qué y cómo se realiza el muestreo, y cualquier cambio de muestreo se encapsula eficientemente dentro de una única clase de fábrica que se interconecta con el resto del sistema.
Del libro Effective Java de Joshua Bloch, parcialmente reescrito por mí:
1) Los métodos de fábrica estáticos ( SFM ), a diferencia de los constructores, tienen nombres.
public static ComplexNumber one () {
return new ComplexNumber(1, 0);
}
public static ComplexNumber imgOne () {
return new ComplexNumber(0, 1);
}
public static ComplexNumber zero () {
return new ComplexNumber(0, 0);
}
2) No se requiere crear un nuevo objeto cada vez que se invoque SFM
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
3) SFM
puede devolver un objeto de cualquier subtipo de su tipo de devolución.
4) SFM
reduce la verbosidad de crear instancias de tipo parametrizadas.
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
Map<String, List<String>> m = HashMap.newInstance();
Factory por sí sola no muestra su belleza tan fácilmente. Es cuando lo combina con otros patrones cuando ve los beneficios reales, por ejemplo, si desea utilizar el patrón de decorador, crear instancias de un objeto directamente puede agregar un acoplamiento adicional a su código. Como dice el profesor de OOP, el acoplamiento es malo :) así que si crearas el objeto decorado y no quisieras aumentar el acoplamiento, podrías utilizar una fábrica.
La idea aquí es separar las preocupaciones: si el código que usa el objeto también tiene suficiente información para crear una instancia, no necesita una fábrica. Sin embargo, si hay alguna lógica o configuración involucrada en la que no desea que el usuario API piense (o se meta), puede ocultar todo eso (y encapsularla para su reutilización) en una fábrica.
Aquí hay un ejemplo: desea acceder a uno de los servicios proporcionados por Google App Engine. El mismo código debería funcionar tanto en el entorno de desarrollo (del que existen dos versiones, maestro-esclavo y de alta disponibilidad) como en un entorno de desarrollo local completamente diferente. Google no quiere contarle sobre el funcionamiento interno de su infraestructura interna, y realmente no quiere saberlo. Entonces, lo que hacen es proporcionar interfaces y fábricas (y varias implementaciones de esas interfaces para que las fábricas puedan elegir que ni siquiera necesitan saber).
Para complementar la respuesta de Thilo, supongamos que tiene un objeto que solo tiene un booleano como constructor: sería una pérdida total construir uno cada vez, ya que solo tiene dos valores posibles.
En este caso, puede crear métodos estáticos de fábrica. La clase Boolean
de Java es un ejemplo: Boolean.valueOf()
.
Personalmente, utilizo el patrón de fábrica cuando la implementación de una interfaz es desconocida en tiempo de ejecución o puede hacerse dinámica.
Esto significa que, como desarrollador, trabajo en contra de una interfaz conocida para la instancia del objeto, pero no me preocupa cómo funciona la implementación.
Toma por ejemplo. Puede usar un patrón de fábrica para proporcionarle objetos desde una base de datos. No le importa si esa base de datos es un archivo plano, una base de datos de usuario local / única, una base de datos de servidor o un recurso web, solo que la fábrica puede generar y administrar esos objetos.
No me gustaría tener que escribir implementaciones para cada uno de esos casos: P
Puede consultar la wikipedia , pero la idea básica de la mayoría de los patrones de diseño es introducir algo de abstracción para lograr una mejor capacidad de mantenimiento y / o reutilización. El patrón de método de fábrica no es una excepción, lo que hace es abstraer la complejidad de la creación del código.
Para el caso simple, parece innecesario utilizar patrones de fábrica, simplemente basta con algo new
. Pero cuando necesita más flexibilidad o funcionalidad, este patrón puede ayudar.
Por ejemplo, a menos que se requiera una nueva instancia, el valor de fábrica estático valueOf(boolean)
es generalmente una mejor opción que el new Bealean(boolean)
, ya que evita la creación de objetos innecesarios. El patrón de método de fábrica también se conoce como Virtual Constructor . Como sabemos, el polimorfismo es una de las características clave de OOP, pero el constructor no puede ser polimórfico, este inconveniente puede superarse mediante un patrón de método de fábrica.
En esencia, instanciar un objeto directamente (típicamente a través de new
) apenas es una implementación concreta, mientras que el patrón de método de fábrica protege una implementación volátil mediante una interfaz estable (no limitada a la interface
en Java), empujando la lógica de creación de objetos detrás de una abstracción para garantizar un código más fácil de mantener y reutilizable.
Como última palabra, para comprender completamente el beneficio del patrón de métodos de fábrica y otros patrones de diseño, es necesario comprender la esencia de OOP, incluida la abstracción de datos , la abstracción polimórfica y el principio SOLID .