2010-11-04 20 views
6

Je suis en train d'assembler une explication et un exemple de code de ce motif de conception, essayant d'aider les autres autour de moi à le saisir (ainsi que de maîtriser le motif).Modèle de conception d'usine (nécessitant une critique)

Ce que je cherche, c'est les opinions & ou la critique de mon explication et l'exemple de code ... merci!

Quel est le modèle d'usine? Le modèle d'usine utilise un «objet créateur d'objet» dédié particulier pour gérer la création d'objets - et la plupart du temps l'instanciation - d'objets similaires à une usine du monde réel.

monde réel exemple
penser à une usine automobile étant le créateur de divers types d'automobiles. Une des chaînes de montage de cette usine automobile peut produire un camion un jour, mais un autre jour peut être ré-usiné pour produire des voitures. Supposons qu'un concessionnaire passe une commande de 10 voitures à son service de gestion de compte. Ce département utilise ensuite une certaine usine et commande les 10 voitures. Les gestionnaires de compte ne sont pas concernés par la fabrication des voitures elles-mêmes (imaginez les résultats médiocres), ils ne travaillent qu'avec le produit final, en s'assurant que le concessionnaire obtient leurs véhicules. Un nouveau modèle de cette même voiture sort l'année suivante et les commandes commencent à affluer. Les gestionnaires de compte (toujours pas concerné par la production de la voiture) passent les commandes, mais maintenant la voiture qu'ils reçoivent est différente, la méthode d'assemblage ou même peut-être l'usine peut être différente, mais les gestionnaires de compte ne doivent pas s'inquiéter à ce sujet. Autre idée: les monteurs d'usine des véhicules peuvent savoir exactement quelle action prendre lorsqu'un certain gestionnaire de compte passe une commande (par exemple, le gestionnaire de compte X passe une commande, l'assembleur d'usine sait que pour le gestionnaire de compte X, ils produisent 10 véhicules de type Y). Une autre option peut être que le gestionnaire de compte indique à l'assembleur exactement quel type de véhicule à produire. Si les gestionnaires de compte traitaient également la création des véhicules (c'est-à-dire qu'ils étaient couplés), chaque fois qu'un véhicule changeait de quelque façon que ce soit, chacun des gestionnaires de compte devait être recyclé pour produire ce véhicule. Cela créerait des problèmes de qualité car il y a beaucoup plus de gestionnaires de comptes qu'il n'y aurait d'usines ... des erreurs se produiraient, les dépenses seraient beaucoup plus importantes.

Entourant Retour à la POO
Une usine d'objet en tant que modèle de conception appliquée à l'ingénierie du logiciel est similaire à l'exemple ci-dessus dans le concept ... L'usine barattes différents types d'autres objets, vous pouvez utiliser une ligne d'assemblage (objet assembleur) qui produit un certain type d'objet, renvoyé d'une certaine manière. L'assembleur peut inspecter le client demandeur et le gérer, ou le client peut indiquer à l'assembleur quel objet il requiert. Maintenant ... vous êtes sur un projet et créez une fabrique d'objets et divers assembleurs, plus tard dans le projet, les exigences changent légèrement, il vous est maintenant demandé de modifier le contenu de l'objet et comment ses clients manipulent cet objet. Étant donné que vous avez utilisé le modèle d'usine, il s'agit d'un simple changement. Dans un emplacement, vous pouvez modifier ou ajouter les objets produits par l'usine et modifier le format dans lequel les assembleurs placent le contenu de l'objet. La manière malheureuse d'avoir fait ceci aurait été sans méthode d'usine, en instanciant chaque instance d'objet et en mettant en forme le contenu d'objet dans les clients eux-mêmes ... disons que vous avez utilisé cet objet particulier dans 20 clients. Maintenant, vous devez aller à chacun des clients, modifier chacune des instances d'objets et des formats ... quelle perte de temps ... Soyez paresseux ... faites-le de la bonne façon la première fois afin de vous sauver (et d'autres) le temps et l'effort plus tard.

Exemple de code (C#)
Voici un exemple en utilisant une usine pour la nourriture et divers objets alimentaires

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 communautaire, peut-être? –

+0

Pourquoi les votes serrés? La question est valide, même si elle est un peu longue :) – jgauffin

Répondre

14

Désolé. C'est une usine assez inflexible. La réflexion peut donner du 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); 
    } 

} 

Utilisation:

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

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

Modifier, des commentaires sur votre code:

Tout d'abord, les usines sont utilisées pour pouvoir créer des objets avec un peu de changements de code que possible lorsque ajouter de nouveaux types d'implémentations. L'utilisation d'une énumération signifie que tous les lieux qui invoquent l'usine doivent utiliser une énumération et être mis à jour lorsque l'énumération change.

Bien sûr, c'est encore un peu mieux que de créer des types directement.

Le deuxième problème avec votre code est que vous utilisez une instruction switch (mais c'est la meilleure façon de le faire si l'enum est une exigence). Il est préférable de pouvoir enregistrer toutes les classes différentes d'une manière ou d'une autre. Soit à partir d'un fichier de configuration, soit en autorisant les implémentations réelles (par exemple la classe Hamburger) à s'enregistrer. Cela nécessite que l'usine suive le modèle singleton.

Voici Réflexion à la rescousse. La réflexion vous permet de parcourir tous les types de DLL et d'EXE. Nous pouvons donc rechercher toutes les classes qui implémente notre interface et donc être capable de construire un dictionnaire pour toutes les classes.

+0

Ceci est le type de commentaires que j'espérais, pouvez-vous commenter sur mon exemple à l'expliquer à quelqu'un qui a jamais entendu parler du modèle avant? Je ne veux pas être sûr que mon exemple est exact et logique. Merci pour la contribution, marquage comme réponse. – dbobrowski

+0

Pourquoi utiliser la réflexion quand vous pouvez utiliser des génériques à la place, à moins que vous ne vouliez lire les types d'aliments à partir d'un fichier ou quelque chose de similaire, l'utilisation de chaînes pour les noms de classes. – Doggett

+0

Dire "hamburger" ne dicte en aucune façon à quoi ressemble l'implémentation. Saying factory.Get () force toutes vos implémentations à dériver de Hamburger. Une autre bonne chose avec l'alternative de chaîne est que le code peut utiliser des implémentations dont il n'a aucune connaissance (par exemple d'un assemblage chargé dynamiquement). – jgauffin

0

Je vous suggère d'utiliser des interfaces à la place des classes/héritage abstrait. A part ça, ça a l'air OK.

+2

Noooooo. Il n'est pas correct d'utiliser des instructions de commutation et d'énumérer – jgauffin

3

Je pense que votre explication, y compris l'exemple du monde réel, est bonne. Cependant, je ne pense pas que votre exemple de code montre les avantages réels du modèle.

Quelques changements possibles:

  • je ne serais pas le ENUM en parallèle aux types. Cela ressemble à vous devez mettre à jour l'énumération chaque fois qu'un type est ajouté. Il pourrait être plus approprié de passer le System.Type. Ensuite, vous pouvez même rendre l'usine générique avec un argument de modèle.
  • Je pense que le motif est plus "impressionnant" si vous l'utilisez pour créer quelque chose comme une interface matérielle. Vous auriez alors un "AbstractNetworkDevice" et tous vos appelants ne savent pas quelle configuration matérielle vous avez. Mais l'usine peut créer un "TcpNetworkDevice" ou un "SerialNetworkDevice" ou quoi que ce soit basé sur une configuration qui a été faite au démarrage.
+0

merci pour les conseils phillipp, je voudrais upvote celui-ci, mais encore trop nouveau – dbobrowski