??xml version="1.0" encoding="utf-8" standalone="yes"?>
我们l写了个单的水果生E序Q描q农场种植水果的q程Q旨在通过此次设计更进一步了解工E设计模式,加强~程的结构化能力?/p>
开发环境:JDK1.5
开发工PJBuilder 2006
二.E序设计介绍
1.E序l构
我们lؓ一个水果公司写了个单的生E序Q该公司专门向市场销售各cL果。我们ؓE序建立了一个名为farm的工E,E序l构比较单,d?个类Qƈ且都攑֜一个默认的包中。其层次l构可从下图体现出来Q?/p>
对各个类的说明:
Fruitc:水果接口Q实现水果方?/p>
Applec:Ҏc,实现Fruit接口
Grapec:葡萄c,实现Fruit接口
Strawberryc:草莓c,实现Fruit接口
FruitGardenerc:园丁c,可种植各U水?/p>
BadFruitExceptionc:要种植的水果不在公司l营的水果范围之内,抛出U植异常
PlantFruitc:实现main()Ҏ
2.E序设计步骤
在这个系l里需要描qC列的水果Q?/p>
葡萄 Grape
草莓 Strawberry
Ҏ Apple
水果与其他的植物有很大的不同Q就是水果最l是可以采摘食用的。那么一个自然的
作法是建立一个各U水果都适用的接口,以便与农场里的其他植物区分开。如下图所C?/p>
水果接口规定出所有的水果必须实现的接口,包括M水果cdd备的ҎQ种植plant()Q生长grow()以及收获harvest()。接口Fruit 的类囑֦下所C?/p>
q个水果接口的源代码如下所C?/p>
代码清单1Q接口Fruit 的源代码
public interface Fruit {
// 生长
void grow();
//收获
void harvest();
//U植
void plant();
}描述Ҏ的Apple cȝ源代码的cd如下所C?/p>
Apple cL水果cȝ一U,因此它实C水果接口所声明的所有方法。另外,׃Ҏ是多q生植物Q因此多Z个treeAge 性质Q描q苹果树的树龄。下面是q个Ҏcȝ源代码?/p>
代码清单2Q类Apple 的源代码
public class Apple
implements Fruit {
private int treeAge;
//生长
public void grow() {
log("Apple is growing...");
}
// 收获
public void harvest() {
log("Apple has been harvested.");
}
//U植
public void plant() {
log("Apple has been planted.");
}
// 辅助Ҏ
public static void log(String msg) {
System.out.println(msg);
}
//树龄的取值方?/p>
public int getTreeAge() {
return treeAge;
}
// 树龄的赋值方?/p>
public void setTreeAge(int treeAge) {
this.treeAge = treeAge;
}
}
同样QGrape cL水果cȝ一U,也实CFruit 接口所声明的所有的Ҏ。但׃葡萄分有c和无籽两种Q因此,比通常的水果多Z个seedless 性质Q如下图所C?/p>
葡萄cȝ源代码如下所C。可以看出,Grape cd样实C水果接口Q从而是水果cd的一U子cd?/p>
代码清单3Q类Grape 的源代码
public class Grape
implements Fruit {
private boolean seedless;
//生长
public void grow() {
log("Grape is growing...");
}
//收获
public void harvest() {
log("Grape has been harvested.");
}
//U植
public void plant() {
log("Grape has been planted.");
}
//辅助Ҏ
public static void log(String msg) {
System.out.println(msg);
}
// 有无c的取值方?/p>
public boolean getSeedless() {
return seedless;
}
//有无c的赋值方?/p>
public void setSeedless(boolean seedless) {
this.seedless = seedless;
}
}
下图所C是Strawberry cȝcd?/p>
Strawberry cdCFruit 接口Q因此,也是水果cd的子cdQ其源代码如下所C?/p>
代码清单4Q类Strawberry 的源代码
public class Strawberry
implements Fruit {
//生长
public void grow() {
log("Strawberry is growing...");
}
//收获
public void harvest() {
log("Strawberry has been harvested.");
}
//U植
public void plant() {
log("Strawberry has been planted.");
}
//辅助Ҏ
public static void log(String msg) {
System.out.println(msg);
}
}
农场的园丁也是系l的一部分Q自然要׃个合适的cL代表。这个类FruitGardener c,其结构由下面的类图描q?/p>
FruitGardener cMҎ客户端的要求Q创建出不同的水果对象,比如ҎQAppleQ,葡萄QGrapeQ或草莓QStrawberryQ的实例。而如果接C合法的要求,FruitGardener cM抛出BadFruitException 异常Q如下图所C?/p>
园丁cȝ源代码如下所C?/p>
代码清单5QFruitGardener cȝ源代?/p>
public class FruitGardener {
//静态工厂方?/p>
public static Fruit factory(String which) throws BadFruitException {
if (which.equalsIgnoreCase("apple")) {
return new Apple();
}
else if (which.equalsIgnoreCase("strawberry")) {
return new Strawberry();
}
else if (which.equalsIgnoreCase("grape")) {
return new Grape();
}
else {
throw new BadFruitException("Bad fruit request");
}
}
}
可以看出Q园丁类提供了一个静态工厂方法。在客户端的调用下,q个Ҏ创徏客户端所需要的水果对象。如果客L的请求是pȝ所不支持的Q工厂方法就会抛Z个BadFruitException 异常。这个异常类的源代码如下所C?/p>
代码清单6QBadFruitException cȝ源代?/p>
public class BadFruitException
extends Exception {
public BadFruitException(String msg) {
super(msg);
}
}
在用时Q客L只需调用FruitGardener 的静态方法factory()卛_。请见下面的C意性客L源代码?/p>
代码清单7Q实现种植即Main()的实?/p>
public class PlantFruit {
public PlantFruit() {
}
public static void main(String[] args) {
PlantFruit plantfruit = new PlantFruit();
try {
//U植葡萄
FruitGardener.factory("grape").plant();
FruitGardener.factory("grape").grow();
FruitGardener.factory("grape").harvest();
System.out.println("==================================");
//U植Ҏ
FruitGardener.factory("apple").plant();
FruitGardener.factory("apple").grow();
FruitGardener.factory("apple").harvest();
System.out.println("==================================");
//U植草莓
FruitGardener.factory("strawberry").plant();
FruitGardener.factory("strawberry").grow();
FruitGardener.factory("strawberry").harvest();
System.out.println("==================================");
}
catch (BadFruitException e) {
}
}
}
到此为止Q我们的单程序已l设计完成,我们可以通过创徏FruitGardener对象来完成水果的U植Q无Z要种什么,只需调用对象中的factory()Ҏ。输出结果如下:
三.单工厂模式的定义
单工厂模式是cȝ创徏模式Q又叫做静态工厂方法(Static Factory MethodQ模式。简单工厂模式是׃个工厂对象决定创建出那一U品类的实例?/p>
四.单工厂模式的l构
单工厂模式是cȝ创徏模式Q这个模式的一般性结构如下图所C?/p>
角色与结?/p>
单工厂模式就是由一个工厂类可以Ҏ传入的参量决定创建出哪一U品类的实例。下图所CZؓ以一个示意性的实现Z说明单工厂模式的l构?/p>
从上囑֏以看出,单工厂模式涉及到工厂角色、抽象品角色以及具体品角色等
三个角色Q?/p>
Q?Q工厂类QCreatorQ角Ԍ担Qq个角色的是工厂Ҏ模式的核心,含有与应用紧
密相关的商业逻辑。工厂类在客L的直接调用下创徏产品对象Q它往往׃?/p>
具体Java cd现?/p>
Q?Q抽象品(ProductQ角Ԍ担Qq个角色的类是工厂方法模式所创徏的对象的?/p>
c,或它们共同拥有的接口。抽象品角色可以用一个Java 接口或者Java 抽象c?/p>
实现?/p>
Q?Q具体品(Concrete ProductQ角Ԍ工厂Ҏ模式所创徏的Q何对象都是这个角
色的实例Q具体品角色由一个具体Java cd现?/p>
工厂cȝC意性源代码如下所C。可以看出,q个工厂Ҏ创徏了一个新的具体品的实例q返q给调用者?/p>
代码清单8QCreator cȝ源代?/p>
public class Creator
{
//静态工厂方?/p>
public static Product factory()
{
return new ConcreteProduct();
}
}
抽象产品角色的主要目的是l所有的具体产品cL供一个共同的cdQ在最单的情况下,可以化ؓ一个标识接口。所谓标识接口,是没有声明MҎ的空接口?/p>
代码清单9Q抽象角色Product 接口的源代码
public interface Product
{
}
具体产品cȝC意性源代码如下?/p>
代码清单10Q具体品角色ConcreteProduct cȝ源代?/p>
public class ConcreteProduct implements Product
{
public ConcreteProduct(){}
}
虽然在这个简单的C意性实现里面只l出了一个具体品类Q但是在实际应用中一般都会遇到多个具体品类的情c?/p>
五.单工厂模式的实现
1Q多层次的品结?/p>
在真实的pȝ中,产品可以形成复杂的等U结构,比如下图所C的树状l构上就有多个抽象品类和具体品类?/p>
q个时候,单工厂模式采取的是以不变应万变的{略Q一律用同一个工厂类。如下图所C?/p>
图中从Factory cd各个Product cȝ虚线代表创徏Q依赖)关系Q从Client 到其他类的联U是一般依赖关pR这样做的好处是设计单,产品cȝ{l构不会反映到工厂类中来Q从而品类的等U结构的变化也就不会影响到工厂类。但是这样做的缺ҎQ增加新的品必导致工厂类的修攏V?/p>
2Q?使用Java 接口或者Java 抽象c?/p>
如果模式所产生的具体品类彼此之间没有共同的商业逻辑Q那么抽象品角色可以由一个Java 接口扮演Q相反,如果q些具体产品cd此之间确有共同的商业逻辑Q那么这些公有的逻辑应当移到抽象角色里面,q就意味着抽象角色应当׃个抽象类扮演。在一个类型的{l构里面Q共同的代码应当量向上UdQ以辑ֈ׃n的目的,如下图所C?/p>
六.模式的优点和~点
1Q?模式的优?/p>
模式的核心是工厂cR这个类含有必要的判断逻辑Q可以决定在什么时候创建哪一个品类的实例。而客L则可以免除直接创Z品对象的责QQ而仅仅负?#8220;消费”产品。简单工厂模式通过q种做法实现了对责Q的分剌Ӏ?/p>
2Q?模式的缺?/p>
正如同在本章前面所讨论的,当品类有复杂的多层ơ等U结构时Q工厂类只有它自
己。以不变应万变,是模式的缺炏V这个工厂类集中了所有的产品创徏逻辑QŞ成一个无所不知的全能类Q有人把q种cd做上帝类QGod ClassQ。如果这个全能类代表的是农场的一个具体园丁的话,那么q个园丁需要对所有的产品负责Q成了农场的关键人物Q他什么时候不能正常工作了Q整个农场都要受到媄响。将q么多的逻辑集中攑֜一个类里面的另外一个缺ҎQ当产品cL不同的接口种cLQ工厂类需要判断在什么时候创建某U品。这U对时机的判断和对哪一U具体品的判断逻辑混合在一P使得pȝ在将来进行功能扩展时较ؓ困难。这一~点在工厂方法模式中得到克服?/p>
׃单工厂模式用静态方法作为工厂方法,而静态方法无法由子类l承Q因此,工厂角色无法形成Zl承的等U结构。这一~点会在工厂Ҏ模式中得到克服?/p>
七.个h体会
设计模式实际上是良好的OO思想的一U提点{每一U设计模式后面都体现了一U良好的OO思\Q这些思\对于解决软g中常见的“change”问题有很大的适应性,而每U模式又有自q特的解决思\Q带有一定的通用性。而组合各U模式又可以解决许多常见问题。不可否认的是,q存在一些未lȝ的设计模式。实际上Q你自己也可以ȝ一些模式出来。无论怎样Q设计模式仍然是面向对象Q它不是C西,也没有必要言必称设计模式—似乎不懂设计模式就落伍了,但给OO的开发者提供一个言意赅的沟通桥梁?/p>
设计模式告诉了我们什么是好的OO思想Q思考如何更好的应用OO的思想—虽然还是那几个耳熟能详的术语:装、ѝ组合、多态?/p>
设计模式首先是对传统的OO使用Ҏ的矫正:如针Ҏ口编E而不是实玎ͼ优先使用l合Q而不是ѝ其ơ是在原来理解上的突_装是对变化而言的,不仅仅是属性和Ҏ的集合。类不仅是现实事物的抽象Q同时它q具有责仅R更有创斎ͼ依赖式注入?/p>
模式不是万能的,也ƈ不总能完美地解决问题,因此每种模式都包括了影响的信息。在应用模式之前Q我们必d分析问题的情境,q评估模式的影响Q再军_是否采用模式Q采用哪一U模式。也是_理解、分析模式,和实现模式一样重?/p>
八.
(1)使用更加通俗易懂的语a解释设计模式Qƈ用完整的代码实例辅以说明。代码的演示旉应g长点Q好让学生看清,看懂代码?/p>
(2)在学生需要的时候给学生补充一点java知识Q有些同学专注于其他语言,对于java也不太懂Q听赯来一头雾_q时候来点知识补充还是必要的?/p>