概述:
Factory Method模式是应用最为广泛的设计模式,毕竟他负责了一系列对象的创建,而对象的创建正是面向对象编程中最为繁琐的行为。GOF在《设计模式》一书写到,“Factory Method模式使一个类的实例化延迟到子类。”准确的说,Factory Method模式是将创建对象实例的责任,转移到了工厂类中,并利用抽象原理,将实例化行为延迟到具体工厂类。
意图:
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method模式使一个类的实例化延迟到子类。
UML图:
常见的例子:
动物--猫、狗、牛、羊、猪........是人都知道,动物它们都是有形状的吧,它们拥有各自不同的外观(Display),是动物他就一定会吃东西(大多数动物还能吃,会吃...)。那具体他们是什么形状的呢?它们用怎么样的方法去吃东西呢?这不是我们所需要关心的,我们所关心的应该是怎么通过工厂去创建具体动物的实例。
案例分析:
现在我们考虑一个日志记录的简单例子,假定我们要设计一个日志记录组件,支持记录的方法有记录到文本文件(TxtWrite)和记录到数据库(DbWrite)两种方式。在我门不考虑设计模式的情况下,这个日志组件的简单实现:
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPattern.FactoryMethod
6{
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 Resolve resolve = new Resolve("txt");
12 resolve.WriteLog("Log message");
13 }
14 }
15
16 class Log
17 {
18 public static void TxtWrite(string message)
19 {
20 Console.WriteLine("TxtWrite():" + message);
21 }
22
23 public static void DbWrite(string message)
24 {
25 Console.WriteLine("DbWrite():" + message);
26 }
27 }
28
29 class Resolve
30 {
31 private string _type;
32 public Resolve(string type)
33 {
34 this._type = type;
35 }
36
37 public void WriteLog(string message)
38 {
39 if (this._type == "txt")
40 {
41 Log.TxtWrite(message);
42 }
43 else
44 {
45 Log.DbWrite(message);
46 }
47 }
48 }
49}
50
很显然这是不符合我们的要求的,一担需求改变,需要增加新的日志记录方式,那就必须得在Log类里增加新的实现方法,而在Resolve类里也同样需要增加新的if(){}语句或是switch(){}语句来做判断处理,这样就引起了整个应用程序的不稳定。如果在深入分析,日志被记录到数据库和文本文件是两种不同的对象,这里有必要、应该的是把他门分开做为单独的对象来处理。
2{
3 public static void TxtWrite(string message)
4 {
5 Console.WriteLine("TxtWrite():" + message);
6 }
7
8 public static void DbWrite(string message)
9 {
10 Console.WriteLine("DbWrite():" + message);
11 }
12}
其实我们可以进一步的为这两重不同的对象抽象出一个共性的父类出来。那到底该怎么去抽象呢?我们在来看看下面这个示例,抽象出一个共性的父类AbstractLog(可把他当作是一个具体的"抽象"产品),结构图如下:
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPattern.FactoryMethod
6{
7 /// <summary>
8 /// 抽象"产品"
9 /// </summary>
10 public abstract class AbstractLog
11 {
12 public abstract List<Log> GetLog();
13 public abstract bool InsertLog(Log log);
14 }
15}
此时DbLog和TxtLog分别继承父类AbstractLog,其定义如下:
2/// 将日志记录到数据库
3/// </summary>
4public class DbLog:AbstractLog
5{
6 public override List<Log> GetLog()
7 {
8 List<Log> list = new List<Log>();
9 list.Add(new Log("被记录到数据库的日志", DateTime.Now));
10 return list;
11 }
12
13 public override bool InsertLog(Log log)
14 {
15 //.实现略..将日志记录到数据库
16 return true;
17 }
18}
19-----------------------------------------------
20/// <summary>
21/// 将日志记录到文本文件
22/// </summary>
23public class TxtLog:AbstractLog
24{
25 /// <summary>
26 /// 获取日志
27 /// </summary>
28 public override List<Log> GetLog()
29 {
30 List<Log> list = new List<Log>();
31 list.Add(new Log("被记录到文本文件的日志", DateTime.Now));
32 return list;
33 }
34
35 /// <summary>
36 /// 插入日志
37 /// </summary>
38 public override bool InsertLog(Log log)
39 {
40 //.实现略..将日志记录到数据库
41 return true;
42 }
43}
我们这样做的好处在哪呢?其实仔细观察就会发现,如果我们现在需要增加把日志记录到XML文件去,那该怎么处理呢?其实只需要新添一个新类并继承于AbstractLog并实现其方法既可,这样既满足了类之间的层次关系,还更好的符合了面向对象设计中的单一职责原则,每一个类都只负责一件具体的事情。
到这里我们的设计可说是完美的了,根据多态原理,我们完全可以像下面这样来调用:
2al.GetLog();
3//
4AbstractLog al1 = new TxtLog();
5al1.GetLog();
6//.
从上面我们不难看出,如果需要改DbLog为TxtLog就必须得new TxtLog(),这样的做法使程序显得很死板,对象的创建不够灵活。此时就需要解耦具体的日志记录方式和应用程序。这就要引入Factory Method模式了,每一个日志记录的对象就是工厂所生成的产品,既然有两种记录方式,那就需要两个不同的工厂去生产了,由于每一个日志的对象所对应的工厂都是负责这个日志对象的创建工作,这里我们是不是又应该抽象出工厂的共性呢?显然答案是肯定有必要的,结构图如下:
代码如下:
2{
3 /// <summary>
4 /// 抽象Log工厂(Creator)
5 /// </summary>
6 public abstract class AbstractLogFactory
7 {
8 public abstract AbstractLog CreateLog();