2010-11-04 11 views
6

Estoy reuniendo un ejemplo de explicación y código de este patrón de diseño, intentando ayudar a otros a mi alrededor a agarrarlo (junto con ayudarme a dominar el patrón también).Patrón de diseño de fábrica (necesidad de crítica)

Lo que estoy buscando son opiniones & o crítica de mi explicación y código de muestra ... gracias!

¿Cuál es el patrón de fábrica? El patrón de fábrica utiliza un "objeto creador de objeto" específico dedicado para manejar la creación de, y la mayoría de las veces, la creación de instancias de objetos, similar a una fábrica del mundo real.

mundo real ejemplo
pensar en una fábrica de automóviles ser el creador de varios tipos de automóviles. Una de las líneas de ensamblaje en esa fábrica de automóviles puede producir un camión un día, pero en otro día puede ser reacondicionado para producir automóviles. Digamos que un concesionario hace un pedido de 10 automóviles al departamento de manejo de cuentas asignado. Ese departamento luego utiliza cierta fábrica y ordena los 10 autos. Los manejadores de cuentas no están interesados ​​en hacer los autos ellos mismos (imagine los malos resultados) solo trabajan con el producto final, asegurando que el concesionario obtenga sus vehículos.

Un nuevo modelo de ese mismo coche salga el próximo año y órdenes comience a fluir. Los ejecutivos de cuentas (todavía no se ocupan de la producción del coche) lugar las órdenes, pero ahora el coche que reciben es diferente, el método de ensamblaje o incluso la fábrica en general puede ser diferente, sin embargo, los controladores de la cuenta no necesitan preocuparse por esto. Una idea adicional: los ensambladores de fábrica de los vehículos pueden saber exactamente qué medidas tomar si un determinado manejador de cuenta realiza un pedido (es decir, el manejador de cuenta X realiza un pedido; el ensamblador de fábrica sabe que para el manejador de cuenta X producen 10 vehículos de tipo Y) Otra opción puede ser que el manejador de la cuenta le dice al ensamblador exactamente qué tipo de vehículo producir.

Si los manejadores de cuenta también manejaron la creación de los vehículos (es decir, estaban acoplados), cada vez que un vehículo cambiaba de alguna forma, cada uno de los manejadores de cuenta tendría que volver a entrenarse para producir ese vehículo. Esto crearía problemas de calidad ya que hay muchos más manejadores de cuentas que fábricas ... los errores serían mayores, los gastos serían mucho mayores.

Dando vueltas de nuevo a la POO
una factoría de objetos como un patrón de diseño aplicado a la ingeniería de software es similar al ejemplo anterior en concepto ... La fábrica produce en serie varios tipos de otros objetos, se puede utilizar una línea de montaje (objeto ensamblador) que produce un cierto tipo de objeto, devuelto de cierta manera. El ensamblador puede inspeccionar el cliente solicitante y el identificador, o el cliente puede decirle al ensamblador qué objeto requiere. Ahora ... usted está en un proyecto y crea una fábrica de objetos y varios ensambladores, más adelante en el proyecto, los requisitos cambian ligeramente, ahora se le pide que modifique los contenidos de los objetos y cómo los manejan sus clientes. Como utilizó el patrón de fábrica, se trata de un cambio simple y en una ubicación, puede cambiar o agregar los objetos que produce la fábrica, y alterar el formato en que los ensambladores colocan los contenidos del objeto.

La forma desafortunada de haber hecho esto hubiera sido sin un método de fábrica, instanciando cada instancia de objeto y formateando el contenido de los objetos en los clientes ... digamos que usaste este objeto en particular en 20 clientes. Ahora debe dirigirse a cada uno de los clientes, alterar cada una de las instancias y formatos de objetos ... qué pérdida de tiempo ... Sea perezoso ... hágalo de la manera correcta la primera vez, así se ahorrará (y otros) el tiempo y esfuerzo más tarde.

ejemplo de código (C#)
A continuación se muestra un ejemplo que utiliza una fábrica de alimentos y alimentos diversos objetos

Factory module 
    public enum FoodType 
    { 
    //enumerated foodtype value, if client wants to specify type of object, coupling still occurs 
     Hamburger, Pizza, HotDog 
    } 
  
    /// <summary> 
    /// Object to be overridden (logical) 
    /// </summary> 
    public abstract class Food 
    { 
     public abstract double FoodPrice { get; } 
    } 
  
    /// <summary> 
    /// Factory object to be overridden (logical) 
    /// </summary> 
    public abstract class FoodFactory 
    { 
     public abstract Food CreateFood(FoodType type); 
    } 
  
    //------------------------------------------------------------------------- 
    #region various food objects 
    class Hamburger : Food 
    { 
     double _foodPrice = 3.59; 
     public override double FoodPrice 
     { 
      get { return _foodPrice; } 
     } 
    } 
  
    class Pizza : Food 
    { 
     double _foodPrice = 2.49; 
     public override double FoodPrice 
     { 
      get { return _foodPrice; } 
     } 
    } 
  
    class HotDog : Food 
    { 
     double _foodPrice = 1.49; 
     public override double FoodPrice 
     { 
      get { return _foodPrice; } 
     } 
    } 
    #endregion 
    //-------------------------------------------------------------------------- 
  
  
    /// <summary> 
    /// Physical factory 
    /// </summary> 
    public class ConcreteFoodFactory : FoodFactory 
    { 
     public override Food CreateFood(FoodType foodType) 
     { 
      switch (foodType) 
      { 
       case FoodType.Hamburger: 
        return new Hamburger(); 
        break; 
       case FoodType.HotDog: 
        return new HotDog(); 
        break; 
       case FoodType.Pizza: 
        return new Pizza(); 
        break; 
       default: 
        return null; 
        break; 
      } 
     } 
    } 
  
    /// <summary> 
    /// Assemblers 
    /// </summary> 
    public class FoodAssembler 
    { 
     public string AssembleFoodAsString(object sender, FoodFactory factory) 
     { 
      Food food = factory.CreateFood(FoodType.Hamburger); 
      if (sender.GetType().Name == "default_aspx") 
      { 
       return string.Format("The price for the hamburger is: ${0}", food.FoodPrice.ToString()); 
      } 
      else 
      { 
       return food.FoodPrice.ToString(); 
      } 
     } 
  
     public Food AssembleFoodObject(FoodFactory factory) 
     { 
      Food food = factory.CreateFood(FoodType.Hamburger); 
      return food; 
     } 
    } 

Calling factory 
FoodFactory factory = new ConcreteFoodFactory(); //create an instance of the factoryenter code here 
lblUser.Text = new FoodAssembler().AssembleFoodAsString(this, factory); //call the assembler which formats for string output 

Object o = new FoodAssembler().AssembleFoodObject(factory); //example: instantiating anon object, initialized with created food object 
+0

wiki de la comunidad, tal vez? –

+0

¿Por qué los votos cercanos? La pregunta es válida, aunque es un poco larga :) – jgauffin

Respuesta

14

Lo sentimos. Esa es una fábrica bastante inflexible. ¡¡¡La reflexión puede dar un poco de POWWAH !!

public interface IFood 
{ 
    bool IsTasty { get; } 
} 
public class Hamburger : IFood 
{ 
    public bool IsTasty {get{ return true;}} 
} 
public class PeaSoup : IFood 
{ 
    public bool IsTasty { get { return false; } } 
} 

public class FoodFactory 
{ 
    private Dictionary<string, Type> _foundFoodTypes = 
     new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase); 

    /// <summary> 
    /// Scan all specified assemblies after food. 
    /// </summary> 
    public void ScanForFood(params Assembly[] assemblies) 
    { 
     var foodType = typeof (IFood); 
     foreach (var assembly in assemblies) 
     { 
      foreach (var type in assembly.GetTypes()) 
      { 
       if (!foodType.IsAssignableFrom(type) || type.IsAbstract || type.IsInterface) 
        continue; 
       _foundFoodTypes.Add(type.Name, type); 
      } 
     } 

    } 

    /// <summary> 
    /// Create some food! 
    /// </summary> 
    /// <param name="name"></param> 
    /// <returns></returns> 
    public IFood Create(string name) 
    { 
     Type type; 
     if (!_foundFoodTypes.TryGetValue(name, out type)) 
      throw new ArgumentException("Failed to find food named '" + name + "'."); 

     return (IFood)Activator.CreateInstance(type); 
    } 

} 

Uso:

var factory = new FoodFactory(); 
factory.ScanForFood(Assembly.GetExecutingAssembly()); 

Console.WriteLine("Is a hamburger tasty? " + factory.Create("Hamburger").IsTasty); 

Editar, información sobre su código:

En primer lugar, las fábricas se utilizan para ser capaz de crear objetos con un poco de cambios en el código como sea posible cuando agregando nuevos tipos de implementaciones. El uso de una enumeración significa que todos los lugares que invocan la fábrica necesitan usar una enumeración y actualizarse cuando la enumeración cambie.

Claro, sigue siendo un poco mejor que crear tipos directamente.

El segundo problema con su código es que está utilizando una declaración de cambio (pero esa es la mejor manera de hacerlo si la enumeración es un requisito). Es mejor poder registrar todas las diferentes clases de alguna manera. Ya sea desde un archivo de configuración o permitiendo que las implementaciones reales (por ejemplo, la clase Hamburger) se registren. Esto requiere que la fábrica siga el patrón de singleton.

Aquí viene Reflexión al rescate. Reflection le permite recorrer todos los tipos en DLL y EXE. De modo que podemos buscar todas las clases que implementen nuestra interfaz y, por lo tanto, poder construir un diccionario para todas las clases.

+0

Este es el tipo de comentarios que esperaba, ¿pueden comentar sobre mi ejemplo al explicárselo a alguien que tiene nunca he oído hablar del patrón antes? Quiero estar seguro de que mi ejemplo es preciso y tiene sentido. Gracias por su entrada, marcando como respuesta. – dbobrowski

+0

¿Por qué utilizar la reflexión cuando puede usar genéricos en su lugar, a menos que desee leer los tipos de alimentos de un archivo o algo similar, el uso de cadenas para los nombres de clases lo hará más propenso a errores. – Doggett

+0

Decir "hamburguesa" no determina de ninguna manera cómo se ve la implementación. Diciendo factory.Get () fuerza a todas sus implementaciones a derivarse de Hamburger. Otra cosa buena con la alternativa de cadena es que el código puede usar implementaciones que no conoce (por ejemplo, desde un ensamblaje cargado dinámicamente). – jgauffin

0

Yo sugeriría que utilice las interfaces en lugar de clases abstractas/herencia. Aparte de eso, se ve bien.

+2

Noooooo. No está bien usar las declaraciones y enum de switch – jgauffin

3

Creo que su explicación, incluido el ejemplo del mundo real, es buena. Sin embargo, no creo que su código de ejemplo muestre los beneficios reales del patrón.

Algunas posibles cambios:

  • yo no tendría la enumeración de forma paralela a los tipos. Parece que tiene que actualizar la enumeración cada vez que se agrega un tipo. Podría ser más apropiado pasar el System.Type. Entonces, incluso puede hacer que la fábrica sea genérica con un argumento de plantilla.
  • Creo que el patrón es más "impresionante" si lo usa para crear algo así como una interfaz de hardware. Luego tendrías un "Dispositivo de red abstracta" y todas las personas que llaman no saben qué configuración de hardware tienes. Pero la fábrica puede crear un "TcpNetworkDevice" o un "SerialNetworkDevice" o lo que sea, según la configuración que se haya realizado al inicio.
+0

gracias por su consejo, phillipp, yo renuncié a este, pero aún demasiado nuevo – dbobrowski

Cuestiones relacionadas