分享
 
 
 

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

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

一、概述

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模式的一个应用实例。)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有