2010-11-04 80 views
6

我一起把这种设计模式的解释和代码示例,试图帮助我身边的人抓住它(与帮助自己掌握模式以及沿)。工厂设计模式(需要批判)

我所寻找的是意见&或批评我的解释和代码示例...谢谢!

什么是工厂模式? 工厂模式利用特定的专用“对象创建对象”来处理的创建 - 和最次的实例化 - 对象,类似于一个真正的世界工厂。

真实世界的例子
想想汽车工厂是各种类型的汽车的创造者。该汽车厂的其中一条生产线可能有一天会生产卡车,但在另一天可能会重新生产汽车。假设经销商向10个汽车订购了他们指定的账户处理部门。该部门然后利用某个工厂并订购了10辆汽车。账户处理人员并不关心自己制造汽车(想象一下效果不佳),他们只能使用最终产品,确保经销商获得他们的车辆。

下一年出现同一辆车的新车型,订单开始流入。帐户处理程序(仍然不关注汽车的生产)发出订单,但现在他们收到的车是不同的,组装方法甚至可能与工厂完全不同,但帐户处理人员不必担心这一点。另外一个想法:车辆的工厂装配可以知道,如果某帐户处理下定单采取什么动作(如帐户处理程序X下订单,工厂汇编知道帐户处理程序X,他们生产的Y型10辆)。另一种选择可能是账户处理程序告诉汇编程序究竟要生产什么类型的车辆。

如果帐户处理程序也处理创建车辆(即它们被耦合),则每次车辆以任何方式改变时,每个帐户处理程序将不得不重新训练以生产该车辆。这会产生质量问题,因为有更多的帐户处理程序比工厂会多......错误会发生,费用会更高。

盘旋回OOP
对象工厂应用于软件工程设计模式是类似于概念上述例子...工厂批量地生产出各种类型的其他的目的,可以利用一个流水线(对象汇编器),它产生一定的对象类型,以某种方式返回。汇编器可以检查请求的客户端并进行处理,或者客户端可以告诉汇编器它需要什么对象。现在......您正在创建一个项目并创建一个对象工厂和各种组装工具,稍后在项目的道路上,需求会稍有变化,现在要求您更改对象内容以及客户如何处理该对象。由于您使用了工厂模式,因此这是一个简单的更改,并且在一个位置,您可以更改或添加工厂生成的对象,并更改汇编器放置对象内容的格式。

不幸的方式这样做本来是没有一个工厂方法,每个实例化对象实例和格式化对象的内容在客户端本身...说你在20个客户端使用该特定对象。现在,你必须去每一个客户端,改变每个对象实例和格式......多么浪费时间......懒惰......第一次以正确的方式做,所以你可以节省自己(和其他人)的时间并在稍后努力。

代码示例(C#)
下面是对象

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,也许? – 2010-11-04 18:39:32

+0

为何选择近距离投票?问题是有效的,虽然它有点长:) – jgauffin 2010-11-04 19:13:49

回答

14

对不起。这是一个相当不灵活的工厂。反射可以激发一些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); 
    } 

} 

用法:

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

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

编辑,您的代码反馈:

首先,工厂用于能够用很少的代码修改为可能的情况下创建对象添加新类型的实现。使用枚举意味着所有调用工厂的地方都需要使用枚举,并在枚举更改时更新。

当然,它仍然比直接创建类型要好一些。

你的代码的第二个问题是你正在使用switch语句(但是如果枚举是一个需求,这是最好的方法)。能够以某种方式注册所有不同的课程会更好。无论是从配置文件,或通过允许实际的实现(例如汉堡类)注册自己。这要求工厂遵循单例模式。

这里是反思救援。反射允许您浏览DLL和EXE中的所有类型。所以我们可以搜索实现我们的接口的所有类,因此能够建立一个字典将所有类。

+0

这是我希望的反馈类型,你可以评论我的例子在解释给谁有从来没有听说过这种模式?我不能确定我的例子是否准确并且合理。感谢您的输入,标记为答案。 – dbobrowski 2010-11-04 19:01:24

+0

为什么在使用反射的时候可以使用泛型,除非你想从文件中读取食物类型或类似的东西,类名字符串的使用只会让它更容易出错。 – Doggett 2010-11-04 23:57:38

+0

说“汉堡包”并不是以任何方式指示实现的样子。说出厂。获得()强制所有的实现从汉堡派生。替代字符串的另一个好处是代码可以使用它不具备任何知识的实现(例如从动态加载的程序集)。 – jgauffin 2010-11-05 08:14:06

0

我会建议你使用接口而不是抽象类/继承利用用于食品和各种食品工厂的例子。除此之外,它看起来不错。

+2

Noooooo。这是不正确的使用开关语句和枚举 – jgauffin 2010-11-04 18:51:00

3

我认为你的解释包括现实世界的例子是好的。但是,我不认为你的示例代码显示了该模式的真正好处。

一些可能发生的变化:

  • 我不会有枚举平行于类型。这看起来像是每次添加类型时必须更新枚举。传递System.Type可能更合适。然后你甚至可以通过模板参数使工厂变得通用。
  • 我认为如果使用它来创建类似硬件接口的东西,该模式更“令人印象深刻”。然后你会得到一个“AbstractNetworkDevice”,你所有的呼叫者都不知道你有哪些硬件设置。但是工厂可以创建一个“TcpNetworkDevice”或者“SerialNetworkDevice”,或者基于启动时所做的一些配置。
+0

感谢您的建议phillipp,我会upvote这一个,但仍然太新 – dbobrowski 2010-11-04 20:13:49