我想说设计模式之工厂模式

      我把设计模式分为三种:构造型、结构型、行为型。这也是基本的分类,我并没有创新,可能名字跟书本上的不一致,但是意思是一样的。那工厂模式属于哪种设计模式?要回答这个问题,那么我们首先必须对设计模式的分类有所了解。下来,我就简单的区分下这三种分类。

      构造型:就是为了把构造对象和使用对象分离开,为什么要分离开?因为要构造的这个对象比较复杂,而且可能多处需要用。另外单独构造对象,其实是实现了构造过程的封装。

      结构型:结构决定功能,为了实现软件复杂的功能,众多对象需要一个合理的结构。那么就得考虑对象之间的关系。对象之间的关系,包含五种:继承、组合、聚合、关联、依赖。为了记住这些东西,我发挥下想象力。继承恰如父母与子女的亲情关系。组合像人体的五脏六腑和人之间的关系。聚合正如刘关张与刘备集团。关联恰似朋友。依赖仿佛同事。我想说的是这个顺序刚好从耦合性最强到最弱,不是吗?

      行为型:如果说,构造型和结构型是静态的,那么行为型是动态的,它体现了对象之间的协作。

接下来,我们谈谈工厂模式,如下图:

 1   public class InnerReferenceEndNoteFactory
 2     {
 3 
 4         public static IInnerReference GetInnerReferenceInstance(ReferenceSystem system)
 5         {
 6             if (system == ReferenceSystem.CodeOrder)
 7             {
 8                 return new InnerReferenceCode();
 9             }
10             else if (system == ReferenceSystem.AuthorYear)
11             {
12                 return new InnerReferenceAuthorYear();
13             }
14            
15         }
16     }
17     public interface IInnerReference
18     {
19         void CreateInnerReference();
20     }

代码说明:上面代码实现了一个简单工厂,定义了一个接口,只有一个方法:创建word文中引文。子类 InnerReferenceCode和 InnerReferenceAuthorYear 实现了这个接口具体实现的代码没有贴出来。工厂内部返回了一个恰当的对象,根据 ReferenceSystem作为条件来判断。ReferenceSystem 是word文中引文的编码模式。而编码模式分为两种:1、顺序编码制 2、作者年度制。所谓顺序编码,就是阿拉伯数字,或者罗马数字等与数字顺序相关的。 作者年度制是先按作者排序,然后按年排序,主要考虑到同一作者在不同年度发表论文的情况。

如何使用该工厂? 直接调用:InnerReferenceEndNoteFactory.GetInnerReferenceInstance(ReferenceSystem.CodeOrder),因为是静态调用,所以我们也可以称静态工厂

不料,我把前辈们的代码重构成这个样子,现在需求又变了。我们要增加脚注的功能。脚注也要实现类似的功能。 那之前的代码,是直接暴露出if..else的判断,然后直接实现各个分支的功能。那现在还要在此基础上更改吗?如果再增添一个类似的功能,我的if..else不知道要嵌套成什么样子。先说尾注的文中引文编码模式两种,脚注也是这两种,增添新的功能,也是这两种模式。我们该如何写代码呢?理清思路,如果我们用面向过程的思维:第一步判断是脚注还是尾注或者是其它,第二步判断是顺序编码制,还是作者年度制。这样我们的代码可能是这样子:

if

   if

   else

else if

   if

   else 

else

   if

   else

如果我们想象下,再增添一种编码制,那这棵树的深度继续增加,到时候势必会增加阅读代码的难度,对于接手代码的人无疑会造成心理压力,那别人可能会心里骂你,把代码都写成这样了。说到这里,我并非说过程思维不好,任何技术或者思想都有适用的场景,面向对象的思想,正是解决复杂问题的一种指导思想。接下来,看看我的重构:

 1    public interface IInnerReferenceFactory
 2     {
 3         IInnerReferenceCode GetInnerReferenceCodeInstance();
 4         IInnerReferenceAuthorYear GetInnerReferenceAuthorYearInstance();
 5     }
 6 
 7     public class InnerReferenceFootNoteFactory : IInnerReferenceFactory
 8     {
 9         public IInnerReferenceCode GetInnerReferenceCodeInstance()
10         {
11             return new InnerNoteFootRerenceCode();
12         }
13 
14         public IInnerReferenceAuthorYear GetInnerReferenceAuthorYearInstance()
15         {
16             throw new NotImplementedException();
17         }
18     }
19 
20     public class InnerReferenceEndNoteFactory:IInnerReferenceFactory
21     {
22         public IInnerReferenceCode GetInnerReferenceCodeInstance()
23         {
24             return new InnerReferenceCode();
25         }
26         public IInnerReferenceAuthorYear GetInnerReferenceAuthorYearInstance()
27         {
28             return new InnerReferenceAuthorYear();
29         }
30     }

 

代码说明:我的工厂开始变化了,变成了抽象的工厂 IInnerReferenceFactory,这个工厂构造两种规格的对象 IInnerReferenceCode 和 IInnerReferenceAuthorYear,够抽象了吧。现在看看我们的具体工厂:InnerReferenceFootNoteFactory ,这个是尾注工厂,因为它实现了抽象的工厂,所以就得生产两种对象,生产的对象必须要满足特定的规格,比如 new InnerNoteFootRerenceCode 这个对象必须得实现 IInnerReferenceCode这个接口。

下来看看如何调用:

 1   public override void CreateInnerReference(FieldData fieldData, List<Bibliography> bibliographies, Journal journalStyle, Dictionary<Guid, string> _dict_Computed = null)
 2         {
 3             ReferenceSystem system = journalStyle.General.RefSystem;
 4             var factory = new InnerReferenceFootNoteFactory();
 5 
 6             if (system == ReferenceSystem.CodeOrder)
 7             {
 8                 factory.GetInnerReferenceCodeInstance().CreateInnerReferenceCode(fieldData, bibliographies, journalStyle, _dict_Computed);
 9             }
10             if (system == ReferenceSystem.AuthorYear)
11             {
12                 factory.GetInnerReferenceAuthorYearInstance().CreateInnerReferenceAuthorYear(fieldData, bibliographies, journalStyle);
13             }
14         }

首先,我们得确定实例化哪个工厂,然后根据编码制确定要生成的对象。因为这部分代码是用来实现脚注功能的,所以实例化哪个工厂其实已经很明确了。这就是抽象工厂。那么有人可能注意到了,为什么不使用工厂模式呢?

我们先来看个图:

正方形代表:作者年度编码制

椭圆:顺序编码制

黑色:尾注

灰色:脚注

次灰色:扩展功能

模式 目前工厂数 增加一个产品族 增加一个产品等级
工厂 4 +2工厂 +2工厂
抽象工厂 2 +1工厂 修改2个工厂

 

 

 

 

从表格可以看出,抽象工厂使用的工厂数比较少,尤其在一个产品族里,产品比较多的情况下,非常适用。

抽象工厂使用的时候,也是根据条件选择合适的工厂,如果我们把这部分代码用简单工厂封装起来,这时候,我们可以称为工厂的工厂模式

 

 

     

posted @ 2016-09-21 14:19  micDavid  阅读(296)  评论(0编辑  收藏  举报