第二章 构建模式Word文件下载.docx
《第二章 构建模式Word文件下载.docx》由会员分享,可在线阅读,更多相关《第二章 构建模式Word文件下载.docx(84页珍藏版)》请在冰点文库上搜索。
在其他文献中,简单工厂往往作为普通工厂模式的一个特例讨论。
在Java语言中,通常的工厂方法模式不能通过设计功能的退化给出静态工厂方法模式。
因为一个方法是不是静态的,对于Java语言来说是一个很大的区别,必须在一开始的时候就加以考虑。
这就是本书将简单工厂单独提出来讨论的一个原因。
学习简单工厂模式是对学习工厂方法模式的一个很好的准备,也是对学习其他模式,特别是单例模式和多例模式的一个很好的准备,这就是本书首先讲解这一模式的另一个原因。
2.1.2简单工厂模式的引进
比如说有一个农场公司,专门向市场销售各类水果。
在这个系统里需要描述下列的水
果:
葡萄Grape
草莓Strawberry
苹果Apple
水果与其他的植物有很大的不同,就是水果最终是可以采摘食用的。
那么一个自然的作法就是建立一个各种水果都适用的接口,以便与农场里的其他植物区分开。
如下图所示。
水果接口规定出所有的水果必须实现的接口,包括任何水果类必须具备的方法:
种植plant(),生长grow()以及收获harvest()。
接口Fruit的类图如下所示。
这个水果接口的源代码如下所示。
代码清单1:
接口Fruit的源代码
publicinterfaceFruit
{
/**
*生长
*/
voidgrow();
*收获
voidharvest();
*种植
voidplant();
}
描述苹果的Apple类的源代码的类图如下所示。
Apple类是水果类的一种,因此它实现了水果接口所声明的所有方法。
另外,由于苹果是多年生植物,因此多出一个treeAge性质,描述苹果树的树龄。
下面是这个苹果类的源代码。
代码清单2:
类Apple的源代码
publicclassAppleimplementsFruit
privateinttreeAge;
publicvoidgrow()
{
log("
Appleisgrowing..."
);
}
publicvoidharvest()
Applehasbeenharvested."
/**
publicvoidplant()
Applehasbeenplanted."
*辅助方法
publicstaticvoidlog(Stringmsg)
System.out.println(msg);
*树龄的取值方法
publicintgetTreeAge()
returntreeAge;
*树龄的赋值方法
publicvoidsetTreeAge(inttreeAge)
this.treeAge=treeAge;
同样,Grape类是水果类的一种,也实现了Fruit接口所声明的所有的方法。
但由于葡萄分有籽和无籽两种,因此,比通常的水果多出一个seedless性质,如下图所示。
葡萄类的源代码如下所示。
可以看出,Grape类同样实现了水果接口,从而是水果类型的一种子类型。
代码清单3:
类Grape的源代码
publicclassGrapeimplementsFruit
privatebooleanseedless;
Grapeisgrowing..."
Grapehasbeenharvested."
Grapehasbeenplanted."
}/**
*有无籽的取值方法
publicbooleangetSeedless()
returnseedless;
*有无籽的赋值方法
publicvoidsetSeedless(booleanseedless)
this.seedless=seedless;
下图所示是Strawberry类的类图。
Strawberry类实现了Fruit接口,因此,也是水果类型的子类型,其源代码如下所示。
代码清单4:
类Strawberry的源代码
publicclassStrawberryimplementsFruit
Strawberryisgrowing..."
Strawberryhasbeenharvested."
*种植
Strawberryhasbeenplanted."
农场的园丁也是系统的一部分,自然要由一个合适的类来代表。
这个类就是
FruitGardener类,其结构由下面的类图描述。
FruitGardener类会根据客户端的要求,创建出不同的水果对象,比如苹果(Apple),葡萄(Grape)或草莓(Strawberry)的实例。
而如果接到不合法的要求,FruitGardener类会抛出BadFruitException异常,如下图所示。
园丁类的源代码如下所示。
代码清单5:
FruitGardener类的源代码
publicclassFruitGardener
*静态工厂方法
publicstaticFruitfactory(Stringwhich)
throwsBadFruitException
if(which.equalsIgnoreCase("
apple"
))
returnnewApple();
elseif(which.equalsIgnoreCase("
strawberry"
returnnewStrawberry();
grape"
returnnewGrape();
else
thrownewBadFruitException("
Badfruitrequest"
}
可以看出,园丁类提供了一个静态工厂方法。
在客户端的调用下,这个方法创建客户端所需要的水果对象。
如果客户端的请求是系统所不支持的,工厂方法就会抛出一个BadFruitException异常。
这个异常类的源代码如下所示。
代码清单6:
BadFruitException类的源代码
publicclassBadFruitExceptionextendsException
publicBadFruitException(Stringmsg)
super(msg);
在使用时,客户端只需调用FruitGardener的静态方法factory()即可。
请见下面的示意性客户端源代码。
代码清单7:
怎样使用异常类BadFruitException
try
FruitGardener.factory("
...
catch(BadFruitExceptione)
这样,农场一定会百果丰收啦!
2.1.3简单工厂模式的结构
简单工厂模式是类的创建模式,这个模式的一般性结构如下图所示。
角色与结构
简单工厂模式就是由一个工厂类可以根据传入的参量决定创建出哪一种产品类的实例。
下图所示为以一个示意性的实现为例说明简单工厂模式的结构。
从上图可以看出,简单工厂模式涉及到工厂角色、抽象产品角色以及具体产品角色等三个角色:
工厂类(Creator)角色:
担任这个角色的是工厂方法模式的核心,含有与应用紧密相关的商业逻辑。
工厂类在客户端的直接调用下创建产品对象,它往往由一个具体Java类实现。
抽象产品(Product)角色:
担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。
抽象产品角色可以用一个Java接口或者Java抽象类实现。
具体产品(ConcreteProduct)角色:
工厂方法模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体Java类实现。
源代码
工厂类的示意性源代码如下所示。
可以看出,这个工厂方法创建了一个新的具体产品的实例并返还给调用者。
代码清单8:
Creator类的源代码
publicclassCreator
publicstaticProductfactory()
returnnewConcreteProduct();
}
抽象产品角色的主要目的是给所有的具体产品类提供一个共同的类型,在最简单的情况下,可以简化为一个标识接口。
所谓标识接口,就是没有声明任何方法的空接口。
关于标识接口的讨论,请参见本书的“专题:
Java接口”一章。
代码清单9:
抽象角色Product接口的源代码
publicinterfaceProduct
具体产品类的示意性源代码如下。
代码清单10:
具体产品角色ConcreteProduct类的源代码
publicclassConcreteProductimplementsProduct
publicConcreteProduct(){}
虽然在这个简单的示意性实现里面只给出了一个具体产品类,但是在实际应用中一般都会遇到多个具体产品类的情况。
2.1.4简单工厂模式的实现
多层次的产品结构
在真实的系统中,产品可以形成复杂的等级结构,比如下图所示的树状结构上就有多个抽象产品类和具体产品类。
这个时候,简单工厂模式采取的是以不变应万变的策略,一律使用同一个工厂类。
图中从Factory类到各个Product类的虚线代表创建(依赖)关系;
从Client到其他类的联线是一般依赖关系。
这样做的好处是设计简单,产品类的等级结构不会反映到工厂类中来,从而产品类的等级结构的变化也就不会影响到工厂类。
但是这样做的缺点是,增加新的产品必将导致工厂类的修改。
请参见后面“模式的优点和缺点”一节,以及本书的“工厂方法(FactoryMethod)模式”一章。
使用Java接口或者Java抽象类
如果模式所产生的具体产品类彼此之间没有共同的商业逻辑,那么抽象产品角色可以由一个Java接口扮演;
相反,如果这些具体产品类彼此之间确有共同的商业逻辑,那么这些公有的逻辑就应当移到抽象角色里面,这就意味着抽象角色应当由一个抽象类扮演。
在一个类型的等级结构里面,共同的代码应当尽量向上移动,以达到共享的目的,如下图所示。
关于抽象类与Java接口的关系与区别,请参见本书的抽象类。
多个工厂方法
每个工厂类可以有多于一个的工厂方法,分别负责创建不同的产品对象。
比如
java.text.DateFormat类是其子类的工厂类,而DateFormat类就提供了多个静态工厂方法。
请参见本章后面的详尽讲解。
抽象产品角色的省略
如果系统仅有一个具体产品角色的话,那么就可以省略掉抽象产品角色。
省略掉抽象产品类后的简略类图如下图所示。
仍然以前面给出的示意性系统为例,这时候系统的类图就变成如下所示。
下面是工厂类的源代码。
显然,这个类提供一个工厂方法,返还一个具体产品类的实例。
代码清单11:
工厂角色的源代码
packagecom.javapatterns.simplefactory.simplified;
publicstaticConcreteProductfactory()
具体产品类的源代码如下所示。
代码清单12:
具体产品角色ConcreteProduct类的源代码
publicclassConcreteProduct
工厂角色与抽象产品角色合并
在有些情况下,工厂角色可以由抽象产品角色扮演。
典型的应用就是java.text.DateFormat类,一个抽象产品类同时是子类的工厂,如下图所示。
本章在下面给出了一个更为详尽的描述。
三个角色全部合并
如果抽象产品角色已经被省略,而工厂角色就可以与具体产品角色合并。
换言之,一个产品类为自身的工厂,如下图所示。
如果仍然以前面讨论过的示意性系统为例,这个系统的结构图如下所示。
显然,三个原本独立的角色:
工厂角色、抽象产品以及具体产品角色都已经合并成为一个类,这个类自行创建自己的实例。
请见下面的源代码。
代码清单13:
具体产品角色与工厂角色合并后的代码
packagecom.javapatterns.simplefactory.simplified1;
这种退化的简单工厂模式与单例模式以及多例模式有相似之处,但是并不等于单例或者多例模式。
关于这一点的讨论请参见本章后面的叙述。
2.2工厂方法(FactoryMethod)模式
工厂方法模式是类的创建模式,又叫做虚拟构造子(VirtualConstructor)模式或者多态性工厂(PolymorphicFactory)模式。
工厂方法模式的用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。
2.2.1工厂方法模式的结构
结构与角色
为了说明工厂方法模式的结构,下面以一个最为简单的情形为例。
这个示意性系统的类图如下图所示。
从上图可以看出,这个使用了工厂方法模式的系统涉及到以下的角色:
Ø
抽象工厂(Creator)角色:
担任这个角色的是工厂方法模式的核心,它是与应用程序无关的。
任何在模式中创建对象的工厂类必须实现这个接口。
在上面的系统中这个角色由Java接口Creator扮演;
在实际的系统中,这个角色也常常使用抽象Java类实现。
具体工厂(ConcreteCreator)角色:
担任这个角色的是实现了抽象工厂接口的具体Java类。
具体工厂角色含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
在本系统中给出了两个这样的角色,也就是具体Java类ConcreteCreator1和ConcreteCreator2。
抽象产品(Product)角色:
工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
在本系统中,这个角色由Java接口Product扮演;
具体产品(ConcreteProduct)角色:
这个角色实现了抽象产品角色所声明的接口。
工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。
在本系统中,这个角色由具体Java类CocnreteProduct1和ConcreteProduct2扮演,它们都实现了Java接口Product。
最后,为了说明这个系统的使用办法,特地引进了一个客户端角色Client。
这个角色创建工厂对象,然后调用工厂对象的工厂方法创建相应的产品对象。
源代码
下面就是这个示意性系统的源代码。
首先是抽象工厂角色的源代码,这个角色是用一个Java接口实现的,它声明了一个工厂方法,要求所有的具体工厂角色都实现这个工厂方法。
抽象工厂角色Creator类的源代码
packagecom.javapatterns.factorymethod;
publicinterfaceCreator
*工厂方法
publicProductfactory();
下面是抽象产品角色的源代码。
由于这里考虑的是最为简单的情形,所以抽象产品角色仅仅为具体产品角色提供一个共同的类型而已,所以是用一个Java标识接口实现的。
一个没有声明任何方法的接口叫做标识接口,关于标识接口的Java接口
抽象产品角色Product类的源代码
下面是具体工厂角色CocnreteCreator1的源代码。
这个角色实现了抽象工所声明的工厂方法。
具体工厂角色ConcreteCreator1类的源代码
publicclassConcreteCreator1implementsCreator
publicProductfactory()
returnnewConcreteProduct1();
下面是具体工厂角色CocnreteCreator2的源代码。
与CocnreteCreator1一实现了抽象工厂角色Creator所声明的工厂方法。
具体工厂角色ConcreteCreator2类的源代码
publicclassConcreteCreator2implementsCreator
returnnewConcreteProduct2();
下面是具体产品角色CocnreteProduct1的源代码。
它是此系统向客户端提供的产品,在通常情况下,这个类会有复杂的商业逻辑。
具体产品角色ConcreteProduct1类的源代码
publicclassConcreteProduct1implementsProduct
publicConcreteProduct1()
//dosomething
类似地,下面是具体产品角色CocnreteProduct2的源代码。
具体产品角色Concrete