大卫的Design Patterns学习笔记02:Factory

 

大卫的Design Patterns学习笔记02:Factory
 
一、概述
Factory(工厂)模式用于封装对象的创建过程,将对象的创建独立成单独的程序模块,从而提高整个应用系统的Flexibility。
 
二、结构
主要有以下三种Factory模式:
1、Simple Factory模式:专门定义一个类来负责创建其它类的实例,被创建的实例通常都具有共同的父类。
图1:Simple Factory的类图
2、Factory Method模式:将对象的创建交由父类中定义的一个标准方法来完成,而不是其构造函数,究竟应该创建何种对象由具体的子类负责决定。
图2:Factory Method的类图
3、Abstract Factory模式:提供一个共同的接口来创建相互关联的多个对象。
图3:Abstract Factory的类图
 
三种Factory模式的区别在于:
Simple Factory在于对产品创建过程的简单封装,它简单地根据输入来决定创建何种产品(这些产品不一定属于同一产品族),因此,任何产品种类的更新都将对Simple Factory的代码造成影响;Factory Method面对的是一个产品族,它引入了ConcreteFactory来决定创建产品族中的何种产品,当产品种类增加时,只需创建新的ConcreteFactory来创建新的产品;而Abstract Factory面对的则是多个产品系列,它是Factory Method的延伸,Abstract Factory在一个ConcreteFactory中包含了多个Factory Method,以用于创建多个不同产品族中的多个产品。
需要注意的是,以上所说的产品并非仅限于单个的产品,可以包括一次创建出来的一组相同或者相关产品,从这个意义上讲,三种Factory特别是Factory Method与Abstract Factory之间的界限并非十分明显。
 
三、应用
三种Factory模式均用于对象的创建,但Factory Method与Abstract Factory由于引进了一个用于定义接口的基类Factory,可以更好地屏蔽内部的创建过程,因此,可以做到客户代码与实现代码的完全隔离(如示例部分的COM类厂);而Simple Factory在这一点上表现则不佳,因此,往往仍然只是客户代码的一部分,只是将对象创建的工作纳入自己管辖之下而已。
但并不是说Abstract Factory就比Simple Factory有用,三种Factory模式的实质是一样的,即用于封装对象的创建过程,在实际使用中应根据自己的需要加以选择。
 
四、优缺点
优点:由于对对象创建过程的封装,不但使得代码的维护工作变得更加简单(职责分离了,明确了,找问题变得更加容易),同时也使得应用系统对于新增产品变得不那么敏感,当新增产品时,我们只需要修改Factory代码或者扩展新的子类来进行产品的创建,灵活性和可重用性都得到了提高。
缺点:由于职责的分离以及类的数目的增多,代码量的增加往往是不可避免的,同时,随着产品种类的变更,类层次将不可避免发生几何膨胀。
 
五、示例
1、Simple Factory示例
<Java Design Patterns A Tutorial>一书中还给出了一个很巧妙的Simple Factory的应用实例。该实例如下:
一般的英文人名有两种写法,"firstname lastname"或者"lastname, firstname",现在我们有一个通讯簿程序需要根据输入的人名信息(可能是上面的任意一种形式)分别解析出输入的firstname和lastname,保存到通讯簿文件中。
如何解决这个问题呢?由于问题本身比较简单,我们可以简单地借助if等来解决这一问题。但是,如果我们的需求突然发生变化,我们还要支持一些其它的形式,如firstname abbreviation.,甚至更多的情况,这时我们怎么办呢?如果我们最初采用的是if等判断语句,显然我们需要修改我们解析数据部分的代码,增加更多的分支,所有的代码可能需要重新进行严格的测试。那么,有没有比较OO,比较灵活的解决方案呢?<Java Design Patterns A Tutorial>给了我们如下的解决方案(Java Code):
 
public class Namer {
       //base class extended by two child classes
       protected String last; //split name
       protected String first; //stored here
 
       public String getFirst() {
              return first; //return first name
       }
       public String getLast() {
              return last; //return last name
       }
}
 
public class FirstFirst extends Namer {
       //extracts first name from last name
       //when separated by a space
       public FirstFirst(String s) {
              int i = s.lastIndexOf(" "); //find sep space
              if (i > 0) {
                     first = s.substring(0, i).trim();
                     last = s.substring(i + 1).trim();
              }
              else {
                     first = ""; // if no space
                     last = s; // put all in last name
              }
       }
}
 
public class LastFirst extends Namer {
       // extracts last name from first name
       // when separated by a comma
       public LastFirst(String s) {
              int i = s.indexOf(","); //find comma
              if (i > 0) {
                     last = s.substring(0, i).trim();
                     first = s.substring(i + 1).trim();
              }
              else {
                     last = s; //if no comma,
                     first = ""; //put all in last name
              }
       }
}
 
public class NamerFactory {
       //Factory decides which class to return based on
       //presence of a comma
       public Namer getNamer(String entry) {
              //comma determines name order
              int i = entry.indexOf(",");
              if (i > 0)
                     return new LastFirst(entry);
              else
                     return new FirstFirst(entry);
       }
}
 
整个实现方案的类图如下:
图4. Namer, NamerFactory及类图
图中采用的是与图1不同的一种结构,是图1所示的基本的SimpleFactory变形后的结果。当新增需求时,我们要做的是从Namer派生新的输入格式封装类,并对NamerFactory中的代码进行简单修改,根据输入创建新的封装类实例。
 
2、Factory Method示例
应用Factory Method的最典型的例子当数COM的类厂,以下是一段典型的COM创建并运用组件提供的接口方法的代码:
IUnknown *pUnk=NULL;
IObject *pObject=NULL;
CoInitialize(NULL);
CoCreateInstance(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IUnknown, (void**)&pUnk);
pUnk->QueryInterface(IID_IOjbect, (void**)&pObject);
pUnk->Release();
pObject->Func();
pObject->Release();
CoUninitialize();
当我们执行上述操作时,类厂在背后默默地为我们完成了创建组件类对象的工作。下面是CoCreateInstance内部实现的伪代码:
CoCreateInstance(....)
{
       .......
       IClassFactory *pClassFactory=NULL;
       CoGetClassObject(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pClassFactory);
       pClassFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
       pClassFactory->Release();
       ........
}
这段话的意思就是先得到类厂对象,再通过类厂创建组件从而得到IUnknown指针。继续深入一步,看看CoGetClassObject的内部伪码:
CoGetClassObject(.....)
{
       //通过查注册表CLSID_Object,得知组件DLL的位置、文件名
       //装入DLL库
       //使用函数GetProcAddress(...)得到DLL库中函数DllGetClassObject的函数指针。
       //调用DllGetClassObject
}
DllGetClassObject是干什么的,它是用来获得类厂对象的,只有先得到类厂才能去创建组件。下面是DllGetClassObject的伪码:
DllGetClassObject(...)
{
       ......
       CFactory* pFactory= new CFactory;   //类厂对象
       //查询IClassFactory指针
       pFactory->QueryInterface(IID_IClassFactory, (void**)&pClassFactory);
       pFactory->Release();
       ......
}
CoGetClassObject的流程已经到此为止,现在返回CoCreateInstance,看看CreateInstance的伪码:
CFactory::CreateInstance(.....)
{
       ...........
       CObject *pObject = new CObject;       //组件对象
       pObject->QueryInterface(IID_IUnknown, (void**)&pUnk);
       pObject->Release();
       ...........
}
以下是上述创建过程的图示:
图5. CoCreateInstance流程图
关于组件创建流程的更为详细的论述,请参见<COM技术内幕>。
如果没有类厂的帮助,而是将组件类的创建工作交由我们自己完成,则我们必须知道组件类的类名,而如果这样,则COM的封装性和可重用性都将大大受损。正是由于运用了Factory Method,COM组件只负责对外提供接口,封装内部实现,包括创建过程的特性才得以完整体现。
 
在MFC中,CDocTemplate是另一个典型的应用Factory Method的例子,MFC实现中通过派生不同的子类CSingleDocTemplate/CMultiDocTemplate来支持不同的文档类型,感兴趣的朋友可以自己研究CDocTemplate及其子类的相关代码,相关代码在afxwin.h中,与上面讨论的Factory Method不同的是,这里的文档类型(单文档/多文档)并没有严格一致的类与之对应。
 
以下是一个简单的Factory Method的例子(Java Code):
 
// Abstract Product Class
abstract class Button {
       public String caption;
       public abstract void paint();
}
 
// ConcreteProduct1
class WinButton extends Button {
       public void paint() {
              System.out.println("I'm a WinButton: " + caption);
       }
}
 
// ConcreteProduct2
class MOTButton extends Button {
       public void paint() {
              System.out.println("I'm a MOTButton: " + caption);
       }
}
 
// Abstract Factory Class
abstract class GUIFactory {
       // Simple Factory Method
       public static GUIFactory getFactory(String type) {
              if (type.equals("WIN")) {
                     return(new WinFactory());
              }
              else if (type.equals("MOT")) {
                     return(new MOTFactory());
              }
              else
                     return null;
       }
       // factory method
       public abstract Button createButton();
}
 
// ConcreteFactory1
class WinFactory extends GUIFactory {
       public Button createButton() {
              return(new WinButton());
       }
}
 
// ConcreteFactory2
class MOTFactory extends GUIFactory {
       public Button createButton() {
              return(new MOTButton());
       }
}
 
class FactoryMethodTest {
       public static void main(String[] args) {
              if (args.length != 1 || (!args[0].equals("WIN") && !args[0].equals("MOT")))   {
                     System.out.println("Usage: java FactoryMethodTest [WIN|MOT]");
                     return;
              }
             
              GUIFactory aFactory = GUIFactory.getFactory(args[0]);
              Button aButton = aFactory.createButton();
              aButton.caption = "Play";
              aButton.paint();
       }
       //output is
       //I'm a WinButton: Play
       //or
       //I'm a MOTButton: Play
}
 
其中,我们通过从抽象基类GUIFactory派生子类WinFactory/MOTFactory分别创建不同的Button类系中的不同产品WinButton/MOTButton。
 
3、Abstract Factory示例
前面已经说过,Abstract Factory只是Factory Method的一种扩展,如果将上面例子中抽象工厂类GUIFactory的接口改成:
// Abstract Factory Class
abstract class GUIFactory {
       // Simple Factory Method
       public static GUIFactory getFactory(String type) {
              ...
       }
       public abstract Button createButton();
       public abstract Button createPanel();
       // more factory methods
}
以对应多个不同的产品类系,并在各子类中实现新增各factory methods,则上面的示例就变成一个Abstract Factory了。
 
(附注:也许看完了本系列的下一篇:Builder模式,再回过头来看这个例子,你会发现如果再增加一个用于组装的Director类,则上面的例子还可以成为Builder模式的一个应用实例。)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值