工厂方法模式(Factory Method)

来源:互联网 发布:服装排料软件 编辑:程序博客网 时间:2024/04/29 10:13

概述

在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口。如何应对这种变化?提供一种封装机制来隔离出“这个易变对象”的变化,从而保持系统中“其它依赖该对象的对象”不随着需求的改变而改变?这就是要说的Factory Method 模式了。

意图

定义一个用户创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。

结构图

生活中的例子

工厂方法定义一个用于创建对象的接口,但是让子类决定实例化哪个类。压注成型演示了这种模式。塑料玩具制造商加工塑料粉,将塑料注入到希望形状的模具中。玩具的类别(车,人物等等)是由模具决定的。

工厂方法解说

在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不接触哪一个产品类被实例化这种细节。这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。在 Factory Method 模式中,工厂类与产品类往往具有平行的等级结构,它们之间一一对应。

现在我们考虑一个日志记录的例子(这里我们只是为了说明 Factory Method 模式,实际项目中的日志记录不会这么去做,也要比这复杂一些)。假定我们要设计日志记录的类,支持记录的方法有 FileLog EventLog 两种方式。在这里我们先不谈设计模式,那么这个日志记录的类就很好实现了:

这样的程序结构显然不能符合我们的要求,如果我们增加一种新的日志记录的方式 DatabaseLog ,那就要修改 Log 类,随着记录方式的变化, switch 语句在不断的变化,这样就引起了整个应用程序的不稳定,进一步分析上面的代码,发现对于 EventLog FileLog 是两种完全不同的记录方式,它们之间不应该存在必然的联系,而应该把它们分别作为单独的对象来对待。


 

 1 ///   <summary>
 2 ///  EventLog类
 3 ///   </summary>

 4 public   class  EventLog
 5 {
 6      public   void  Write()
 7      {
 8         Console.WriteLine( " EventLog Write Success! " );
 9     }

10 }

11
12 ///   <summary>
13 ///  FileLog类
14 ///   </summary>

15 public   class  FileLog
16 {
17      public   void  Write()
18      {
19         Console.WriteLine( " FileLog Write Success! " );
20     }

21 }

22

进一步抽象,为它们抽象出一个共同的父类,结构图如下:

实现代码: FileLog 类的代码应该如下:

1 ///   <summary>
2 ///  Log类
3 ///   </summary>

4 public   abstract   class  Log
5 {
6      public   abstract   void  Write();
7 }

8

此时EventLog

DatabaseLog 的时候,需要做哪些事情?只需要增加一个继承父类 Log 的子类来实现,而无需再去修改 EventLog FileLog 类,这样的设计满足了类之间的层次关系,又很好的符合了面向对象设计中的单一职责原则,每一个类都只负责一件具体的事情。到这里似乎我们的设计很完美了,事实上我们还没有看客户程序如何去调用。 在应用程序中,我们要使用某一种日志记录方式,也许会用到如下这样的语句: EventLog 变化为 FileLog ,我们就得修改所有程序代码中出现上面语句的部分,这样的工作量是可想而知的。此时就需要解耦具体的日志记录方式和应用程序。这就要引入 Factory Method 模式了,每一个日志记录的对象就是工厂所生成的产品,既然有两种记录方式,那就需要两个不同的工厂去生产了,代码如下:

 1 ///   <summary>
 2 ///  EventLog类
 3 ///   </summary>

 4 public   class  EventLog:Log
 5 {
 6      public   override   void  Write()
 7      {
 8         Console.WriteLine( " EventLog Write Success! " );
 9     }

10 }

11 ///   <summary>
12 ///  FileLog类
13 ///   </summary>

14 public   class  FileLog:Log
15 {
16      public   override   void  Write()
17      {
18         Console.WriteLine( " FileLog Write Success! " );
19     }

20 }

21

此时我们再看增加新的记录日志方式

EventLog eventlog  =   new  EventLog();
eventlog.Write();

当日志记录的方式从

 

 1 ///   <summary>
 2 ///  EventFactory类
 3 ///   </summary>

 4 public   class  EventFactory
 5 {
 6      public  EventLog Create()
 7      {
 8          return   new  EventLog();
 9     }

10 }

11 ///   <summary>
12 ///  FileFactory类
13 ///   </summary>

14 public   class  FileFactory
15 {
16      public  FileLog Create()
17      {
18          return   new  FileLog();
19     }

20 }

21

这两个工厂和具体的产品之间是平行的结构,并一一对应,并在它们的基础上抽象出一个公用的接口,结构图如下:

实现代码如下:

1 ///   <summary>
2 ///  LogFactory类
3 ///   </summary>

4 public   abstract   class  LogFactory
5 {
6      public   abstract  Log Create();
7 }

8

此时两个具体工厂的代码应该如下:

Factory Method 的过程。这样达到了我们应用程序和具体日志记录对象之间解耦的目的了吗?看一下此时客户端程序代码:

 1 ///   <summary>
 2 ///  EventFactory类
 3 ///   </summary>

 4 public   class  EventFactory:LogFactory
 5 {
 6      public   override  EventLog Create()
 7      {
 8          return   new  EventLog();
 9     }

10 }

11 ///   <summary>
12 ///  FileFactory类
13 ///   </summary>

14 public   class  FileFactory:LogFactory
15 {
16      public   override  FileLog Create()
17      {
18          return   new  FileLog();
19     }

20 }

21

这样通过工厂方法模式我们把上面那对象创建工作封装在了工厂中,此时我们似乎完成了整个

Log 对象的创建是频繁的,在这里我们可以把

 1 ///   <summary>
 2 ///  App类
 3 ///   </summary>

 4 public   class  App
 5 {
 6      public   static   void  Main( string [] args)
 7      {
 8         LogFactory factory  =   new  EventFactory();
 9
10         Log log  =  factory.Create();
11
12         log.Write();
13     }

14 }

15

在客户程序中,我们有效地避免了具体产品对象和应用程序之间的耦合,可是我们也看到,增加了具体工厂对象和应用程序之间的耦合。那这样究竟带来什么好处呢?我们知道,在应用程序中,

LogFactory factory = new EventFactory();

这句话放在一个类模块中,任何需要用到 Log 对象的地方仍然不变。要是换一种日志记录方式,只要修改一处为:

LogFactory factory = new FileFactory();

其余的任何地方我们都不需要去修改。有人会说那还是修改代码,其实在开发中我们很难避免修改,但是我们可以尽量做到只修改一处。

其实利用 .NET 的特性,我们可以避免这种不必要的修改。下面我们利用 .NET 中的反射机制来进一步修改我们的程序,这时就要用到配置文件了,如果我们想使用哪一种日志记录方式,则在相应的配置文件中设置如下:

1 < appSettings >
2      < add  key ="factoryName"  value ="EventFactory" ></ add >
3 </ appSettings >
4

此时客户端代码如下:

 

 1 ///   <summary>
 2 ///  App类
 3 ///   </summary>

 4 public   class  App
 5 {
 6      public   static   void  Main( string [] args)
 7      {
 8          string  strfactoryName  =  ConfigurationSettings.AppSettings[ " factoryName " ];
 9         
10         LogFactory factory;
11         factory  =  (LogFactory)Assembly.Load( " FactoryMethod " ).CreateInstance( " FactoryMethod. "   +  strfactoryName);
12
13         Log log  =  factory.Create();
14         log.Write();
15     }

16 }

17

现在我们看到,在引进新产品(日志记录方式)的情况下,我们并不需要去修改工厂类,而只是增加新的产品类和新的工厂类(注意:这是任何时候都不能避免的),这样很好的符合了开放封闭原则。