设计模式之三个工厂

来源:互联网 发布:sql语句教程 编辑:程序博客网 时间:2024/05/16 08:20
在学习设计模式的过程中,看到了三个模式很相似的名称,这一篇博客,就拿它们在一起说说。首先,这三个模式的名称分别是简单工厂、工厂方法和抽象工厂模式。对于前两个模式,感觉还比较好理解,抽象工厂模式真的很糊涂。也许,等这篇博客完工后,自己对它会更亲密些了。首先说第一个简单工厂模式。从类型上来说,它应该属于创建型模式,但它并不包含在上篇23种模式总结的范围之中。至于为什么,自己也不清楚。这也就让自己想到了一个问题,模式的分类为什么分为创建、结构和行为三大类呢?这个问题可以在第三遍设计模式的时候思考思考。言归正传,还是回到简单工厂模式。简单工厂模式】其是由一个工厂对象决定创建出哪一种产品类的实例。● 该模式中包含的角色及其职责① 工厂(Creator)角色:该模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。② 抽象产品(Product)角色:该模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。③ 具体产品(Concrete Product)角色:该模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。● 该模式的最大优点工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。● 该模式的缺点由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则需要改变工厂类,也就相应地违背了开放-封闭原则。● 该模式何时使用① 工厂类负责创建的对象比较少;② 客户只知道传入工厂类的参数,对于如何创建对象不关心;③ 由于很容易违反设计原则,所以一般只在很简单的情况下应用。工厂方法模式】其定义一个用于创建对象的接口,让子类决定实例化哪一个类。其使一个类的实例化延迟到其子类。● 该模式中包含的角色及其职责:① 工厂接口:该模式的核心,与调用者直接交互用来提供产品。② 工厂实现:在编程中,其决定如何实例化产品。需要有多少种产品,就需要有多少个具体的工厂实现。③ 产品接口:其主要目的是定义产品的规范,所有的产品实现都必须遵循产品接口定义的规范。④ 产品实现:实现产品接口的具体类,决定了产品在客户端中的具体行为。● 该模式的优点① 可以使代码结构清晰,有效地封装变化。② 对调用者屏蔽具体的产品类。如果使用工厂模式,调用者只关心产品的接口就可以。至于具体的实现,调用者无需关心。即使变更了具体的实现,对调用者来说没有任何影响。③ 降低耦合度。下面,就以大鸟和小菜一开始就讨论的一个计算器的程序为例,也正是因为这个程序,我才把更加清楚它们两个。先用简单工厂模式的方法看如何实现计算器:首先包含一个运算类:
[csharp] view plaincopy
  1. public class Operation  
  2.         {  
  3.             private double _numberA = 0;  
  4.             private double _numberB = 0;  
  5.             public double NumberA  
  6.             {  
  7.                 get { return _numberA; }  
  8.                 set { _numberA = value; }  
  9.             }  
  10.             public double NumberB  
  11.             {  
  12.                 get { return _numberB; }  
  13.                 set { _numberB = value; }  
  14.             }  
  15.             public virtual double GetResult()  
  16.             {  
  17.                 double result = 0;  
  18.                 return result;  
  19.             }  
  20.         }  
其次各个具体的加减乘除类都继承运算类:
[csharp] view plaincopy
  1. class OperationAdd : Operation  
  2.        {  
  3.            public override double GetResult()  
  4.            {  
  5.                double result = 0;  
  6.                result = NumberA + NumberB;  
  7.                return result;  
  8.            }  
  9.        }  
  10.        class OperationSub : Operation  
  11.        {  
  12.            public override double GetResult()  
  13.            {  
  14.                double result = 0;  
  15.                result = NumberA - NumberB;  
  16.                return result;  
  17.            }  
  18.        }  
最后利用一个运算工厂类负责其中的实例化:
[csharp] view plaincopy
  1. public class OperationFactory  
  2.         {  
  3.             public static Operation createOperate(string operate)  
  4.             {  
  5.                 Operation oper = null;  
  6.                 switch (operate)  
  7.                 {  
  8.                     case "+":  
  9.                         oper = new OperationAdd();  
  10.                         break;  
  11.                     case "-":  
  12.                         oper = new OperationSub();  
  13.                         break;  
  14.                     case "*":  
  15.                         oper = new OperationMul();  
  16.                         break;  
  17.                     case "/":  
  18.                         oper = new OperationDiv();  
  19.                         break;  
  20.                 }  
  21.                 return oper;  
  22.             }  
  23.         }  
这样的代码确实比面向过程方便很多,但是还是存在问题,比如现在想要增加一个其他的具体运算类,如求根,这下,就必须依靠修改代码去实现实例化,这也就违背了只扩展不修改的原则。办法总比问题多,这样,第二个说的工厂方法模式就可以用来解决问题了。下面看如何利用工厂方法模式的方法是如何实现计算器的:首先是前者方法中的运算类和各个具体运算类保持不变;其次增加一个工厂接口:
[csharp] view plaincopy
  1. interface IFactory  
  2. {  
  3.        Operation CreateOperation();  
  4. }  
最后加减乘除各建一个具体的工厂去实现这个接口:
[csharp] view plaincopy
  1. //加法类工厂  
  2. class AddFactory:IFactory  
  3. {  
  4.         public Operation CreateOperation()  
  5.         {  
  6.               return new OperationAdd();  
  7.         }  
  8. }  
  9.   
  10. //减法类工厂  
  11. class SubFactory:IFactory  
  12. {  
  13.         public Operation CreateOperation()  
  14.         {  
  15.               return new OperationAdd();  
  16.         }  
  17. }  
这样的代码就解决了上面去修改代码的问题,如果是要增加一个其他的具体运算类,只需要添加一个它的运算工厂以及具体运算类本身就行,也就是扩展就行。从两者的结构图看,也更加清晰一些:
到此,我们也可以很容易发现其实它们两者是极为相似的,区别也就是:简单工厂只有三个要素,它没有工厂接口,所以在工厂实现的扩展性方面弱,可以算是工厂方法模式的简化版吧。第三个,抽象工厂模式。抽象工厂模式】提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。● 该模式何时使用:当每个抽象产品都有多于一个的具体子类的时候,工厂角色怎么知道实例化哪一个子类呢?:读到这句话,自己才明白那么一点为什么有抽象工厂模式的出现了。其提供两个具体工厂角色,分别对应于这两个具体产品角色,每一个具体工厂产品角色只负责一个产品角色的实例化。每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。● 该模式优点① 分离了具体的类;② 使得易于交换产品系列;③ 有利于产品的一致性。● 该模式缺点难以支持新种类的产品对于其优点还是有些体会的。下面以大鸟和小菜讨论的访问数据的例子,看看它与工厂方法模式间的差别。先看用工厂方法模式实现数据访问程序:其中包含IUser接口(用于客户端访问)和IFactory接口(用于访问User表对象的抽象的工厂接口)
[csharp] view plaincopy
  1. //IUser接口  
  2. interface IUser  
  3. {  
  4.      void Insert(User user);  
  5.        
  6.      User GetUser(int id);  
  7. }  
  8. //IFactory接口  
  9. interface IFactory  
  10. {  
  11.      IUser CreateUser();  
  12. }  
另外,还有具体类(AccessUser)和具体工厂(AccessFactory)。
[csharp] view plaincopy
  1. //AccessUser类  
  2. class AccessUser:IUser  
  3. {  
  4.      public void Insert(User user)  
  5.      {  
  6.          Console.WriteLine("在Access中给User表增加一条记录");  
  7.       }  
  8.       public User GetUser(int id)  
  9.       {  
  10.           Console.WriteLine("在Access中根据ID得到User表一条记录");  
  11.           return null;  
  12.        }  
  13. }  
  14. //AccessFactory类  
  15. class AccessFactory:IFactory  
  16. {  
  17.        public IUser CreateUser()  
  18.        {  
  19.              return new AccessUser();  
  20.        }  
  21. }  
这样,就用工厂方法模式实现了对数据的访问。下面同样一个问题:如果要增加某一具体类,在这里也就是指访问数据库中的其他表呢?这就必将增加一个个类对应一个个表。这时候,抽象工厂模式就来解决问题了。它实现不光是对User表的访问,同样访问Department表的访问。大体上都和工厂方法一致,只是它又通过增加接口来实现。同样看看两者的结构图:
最后,大鸟和小菜的对话,很好地说明了两者的区别。小菜以为后者也是工厂方法模式的实现,大鸟的回答是:“只有一个User类和User操作类的时候,是只需要工厂方法模式的,但是现在数据库中有很多表,而SQL Server与Access又是两大不同的分类,所以解决这种涉及到多个产品系列的问题,有一个专门的工厂模式叫做抽象工厂模式。
至此,三个工厂的总结就结束了。现在,也确实对三个工厂的了解更加深入了些。学习这三个模式,也让我们看到了遇到问题,就相应地解决问题的重要性,否则编写的程序永远不会有所进步的。
0 0
原创粉丝点击