2009-05-08 13 views
6

Al implementar una fábrica o fábrica simple, ¿qué iría en contra de usar un Tipo en lugar de un Enum para especificar la clase para instanciar?Patrón de fábrica: ¿Enumeraciones o tipos?

Por ejemplo

public class SimpleFactory 
{ 
public static ITest Create(Type type) 
{ 
    if (type == typeof(ConcreteTest1)) 
    return new ConcreteTest1(); 
    if (type == typeof(ConcreteTest2)) 
    return new ConcreteTest2(); 

    throw new Exception("Invalid type"); 
} 
} 

Respuesta

15

Uso de una enumeración es más restrictiva, lo que significa que es menos probable que el usuario va a tratar de utilizar su fábrica con un tipo no soportado.

Me parece que es bueno hacer todo lo posible al definir una API para desalentar los patrones de uso que harán que se generen excepciones. Permitiendo "Tipo" en este caso se abre a millones de formas de llamar a la función que se traducirá en:

throw new Exception("Invalid type"); 

Usando una enumeración eliminaría esto. La única forma en que arrojaría una enumeración sería si el usuario hiciera algo notoriamente incorrecto.

+2

¡Definitivamente estoy de acuerdo! Las restricciones de tiempo de compilación son mucho más fáciles de aplicar y más fáciles de usar. – jeremyalan

2

si desea una fábrica a prueba de tontos, debe crear una fábrica de concreto para cada tipo de concreto. Esta clase no sigue el principio de apertura cerrada: cada vez que obtienes un nuevo tipo de hormigón, debes volver a editar esta clase.

En mi humilde opinión un mejor enfoque es el uso de la herencia, una clase de fábrica de hormigón para cada tipo de hormigón.

+0

¿Cómo se decide qué fábrica de concreto usar? ¿Todavía necesitarías los condicionales anteriores? – theringostarrs

+0

Si utiliza una clase de tipo FactoryLoader, que contiene los condicionales anteriores para que la lógica se elimine de las propias fábricas de hormigón, ¿cómo evitar la violación del principio de abierto/cerrado al agregar un nuevo condicional/tipo de fábrica a la lógica de FactoryLoader? ¿No estamos simplemente trasladando el problema a otra parte? – theringostarrs

+0

Supongo que un contenedor de IoC se encargaría de esto, pero ¿cómo harías esto de una manera pura? – theringostarrs

1

Si desea crear por tipo, puede usar Activator.CreateInstance(Type t). Envuélvalo en un método de plantilla para limitarlo a su interfaz, algo así como Create<T> where T:ITest.

2

Preferiría usar una restricción genérica, por el hecho de que tener una enumeración para especificar qué tipo de objeto quieres me parece redundante, y con el uso de un tipo como el que describes, violas el Abierto/Cerrado principio. Lo que haría diferente de lo que ha hecho allí es restringir su tipo para que solo se puedan pasar los tipos permisibles.

Daré un ejemplo en C# utilizando genéricos.

public class SimpleFactory 
{ 
public static ITest Create<T>() 
    where T: ITest, new() 
{ 
    return new T(); 
} 
} 

allí tendría que aplicar IConcreteTest tanto con ConcreteTest1 y ConcreteTest2 y se podía usar su fábrica de la siguiente manera:

ConcreteTest1 test1 = SimpleFactory.Create<ConcreteTest1>(); 
+0

¿Cómo se soluciona el problema pasando el tipo a un método genérico? ¿Eso todavía no requiere un conocimiento avanzado de T por la persona que llama? – jeremyalan

+0

Sí, pero en su ejemplo ya conoce el tipo, lo está pasando al método. La única diferencia es que la estoy usando como un genérico, así que puedes hacer algo como SimpleFactory.Create (); – Joseph

+0

No ha dado la implementación de typeof, por lo que no podemos estar seguros de que tenga un conocimiento previo de los tipos y pueda pasar a la llamada. – Rohit

1

creo que la mayor preocupación que tengo es que el propósito de la fábrica es permitir que el código del cliente cree una instancia derivada de un objeto sin conocer los detalles del tipo que se está creando (más específicamente, los detalles de cómo crear la instancia, pero si se hace correctamente, la persona que llama no debería necesitar conocer ninguno de los más finos) detalles más allá de lo que proporciona la clase base).

El uso de información de tipo extraída del tipo derivado todavía requiere que el llamante tenga un conocimiento íntimo sobre qué tipo quiere crear una instancia, lo que dificulta su actualización y mantenimiento. Al sustituir un tipo Enum (o cadena, int, etc.), puede actualizar la fábrica sin tener que actualizar el código de llamada para conocer los nuevos tipos derivados.

Supongo que se podría argumentar que el nombre de tipo podría leerse como una cadena de un archivo de configuración, base de datos, etc., y la información de tipo determinada usando Reflections (en .NET) o RTTI (en C++), pero Creo que este es un mejor caso para simplemente usar el tipo de cadena como su identificador, ya que efectivamente servirá para el mismo propósito.

4

Las fábricas solo son útiles si realizan la configuración o la inicialización en los objetos para ponerlos en un estado válido. No me molestaría con una fábrica si todo lo que hace es actualizar y devolver objetos.

Crearía una fábrica para cada jerarquía de clases. Por ejemplo:

public abstract class Vehicle {} 
public class Car : Vehicle {} 
public class Truck : Vehicle {} 

public class VehicleFactory 
{ 
    public Vehicle CreateVehicle<T>() where T : Vehicle 
    { 
     // Get type of T and delegate creation to private methods 
    } 
} 
Cuestiones relacionadas