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