一. 程序设计目标
我们组写了个简单的水果生产程序,描述农场种植水果的过程,旨在通过此次设计更进一步了解工程设计模式,加强编程的结构化能力。
开发环境:JDK1.5
开发工具:JBuilder 2006
二.程序设计介绍
1.程序结构
我们组为一个水果公司写了个简单的生产程序,该公司专门向市场销售各类水果。我们为程序建立了一个名为farm的工程,程序结构比较简单,总共有7个类,并且都放在一个默认的包中。其层次结构可从下图体现出来:
对各个类的说明:
Fruit类:水果接口,实现水果方法
Apple类:苹果类,实现Fruit接口
Grape类:葡萄类,实现Fruit接口
Strawberry类:草莓类,实现Fruit接口
FruitGardener类:园丁类,可种植各种水果
BadFruitException类:要种植的水果不在公司经营的水果范围之内,抛出种植异常
PlantFruit类:实现main()方法
2.程序设计步骤
在这个系统里需要描述下列的水果:
葡萄 Grape
草莓 Strawberry
苹果 Apple
水果与其他的植物有很大的不同,就是水果最终是可以采摘食用的。那么一个自然的
作法就是建立一个各种水果都适用的接口,以便与农场里的其他植物区分开。如下图所示。
水果接口规定出所有的水果必须实现的接口,包括任何水果类必须具备的方法:种植plant(),生长grow()以及收获harvest()。接口Fruit 的类图如下所示。
这个水果接口的源代码如下所示。
代码清单1:接口Fruit 的源代码
public interface Fruit {
// 生长
void grow();
//收获
void harvest();
//种植
void plant();
}描述苹果的Apple 类的源代码的类图如下所示。
Apple 类是水果类的一种,因此它实现了水果接口所声明的所有方法。另外,由于苹果是多年生植物,因此多出一个treeAge 性质,描述苹果树的树龄。下面是这个苹果类的源代码。
代码清单2:类Apple 的源代码
public class Apple
implements Fruit {
private int treeAge;
//生长
public void grow() {
log("Apple is growing...");
}
// 收获
public void harvest() {
log("Apple has been harvested.");
}
//种植
public void plant() {
log("Apple has been planted.");
}
// 辅助方法
public static void log(String msg) {
System.out.println(msg);
}
//树龄的取值方法
public int getTreeAge() {
return treeAge;
}
// 树龄的赋值方法
public void setTreeAge(int treeAge) {
this.treeAge = treeAge;
}
}
同样,Grape 类是水果类的一种,也实现了Fruit 接口所声明的所有的方法。但由于葡萄分有籽和无籽两种,因此,比通常的水果多出一个seedless 性质,如下图所示。
葡萄类的源代码如下所示。可以看出,Grape 类同样实现了水果接口,从而是水果类型的一种子类型。
代码清单3:类Grape 的源代码
public class Grape
implements Fruit {
private boolean seedless;
//生长
public void grow() {
log("Grape is growing...");
}
//收获
public void harvest() {
log("Grape has been harvested.");
}
//种植
public void plant() {
log("Grape has been planted.");
}
//辅助方法
public static void log(String msg) {
System.out.println(msg);
}
// 有无籽的取值方法
public boolean getSeedless() {
return seedless;
}
//有无籽的赋值方法
public void setSeedless(boolean seedless) {
this.seedless = seedless;
}
}
下图所示是Strawberry 类的类图。
Strawberry 类实现了Fruit 接口,因此,也是水果类型的子类型,其源代码如下所示。
代码清单4:类Strawberry 的源代码
public class Strawberry
implements Fruit {
//生长
public void grow() {
log("Strawberry is growing...");
}
//收获
public void harvest() {
log("Strawberry has been harvested.");
}
//种植
public void plant() {
log("Strawberry has been planted.");
}
//辅助方法
public static void log(String msg) {
System.out.println(msg);
}
}
农场的园丁也是系统的一部分,自然要由一个合适的类来代表。这个类就FruitGardener 类,其结构由下面的类图描述。
FruitGardener 类会根据客户端的要求,创建出不同的水果对象,比如苹果(Apple),葡萄(Grape)或草莓(Strawberry)的实例。而如果接到不合法的要求,FruitGardener 类会抛出BadFruitException 异常,如下图所示。
园丁类的源代码如下所示。
代码清单5:FruitGardener 类的源代码
public class FruitGardener {
//静态工厂方法
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");
}
}
}
可以看出,园丁类提供了一个静态工厂方法。在客户端的调用下,这个方法创建客户端所需要的水果对象。如果客户端的请求是系统所不支持的,工厂方法就会抛出一个BadFruitException 异常。这个异常类的源代码如下所示。
代码清单6:BadFruitException 类的源代码
public class BadFruitException
extends Exception {
public BadFruitException(String msg) {
super(msg);
}
}
在使用时,客户端只需调用FruitGardener 的静态方法factory()即可。请见下面的示意性客户端源代码。
代码清单7:实现种植即Main()的实现
public class PlantFruit {
public PlantFruit() {
}
public static void main(String[] args) {
PlantFruit plantfruit = new PlantFruit();
try {
//种植葡萄
FruitGardener.factory("grape").plant();
FruitGardener.factory("grape").grow();
FruitGardener.factory("grape").harvest();
System.out.println("==================================");
//种植苹果
FruitGardener.factory("apple").plant();
FruitGardener.factory("apple").grow();
FruitGardener.factory("apple").harvest();
System.out.println("==================================");
//种植草莓
FruitGardener.factory("strawberry").plant();
FruitGardener.factory("strawberry").grow();
FruitGardener.factory("strawberry").harvest();
System.out.println("==================================");
}
catch (BadFruitException e) {
}
}
}
到此为止,我们的简单程序已经设计完成,我们可以通过创建FruitGardener对象来完成水果的种植,无论你要种什么,只需调用对象中的factory()方法。输出结果如下:
三.简单工厂模式的定义
简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出那一种产品类的实例。
四.简单工厂模式的结构
简单工厂模式是类的创建模式,这个模式的一般性结构如下图所示。
角色与结构
简单工厂模式就是由一个工厂类可以根据传入的参量决定创建出哪一种产品类的实例。下图所示为以一个示意性的实现为例说明简单工厂模式的结构。
从上图可以看出,简单工厂模式涉及到工厂角色、抽象产品角色以及具体产品角色等
三个角色:
(1)工厂类(Creator)角色:担任这个角色的是工厂方法模式的核心,含有与应用紧
密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个
具体Java 类实现。
(2)抽象产品(Product)角色:担任这个角色的类是工厂方法模式所创建的对象的父
类,或它们共同拥有的接口。抽象产品角色可以用一个Java 接口或者Java 抽象类
实现。
(3)具体产品(Concrete Product)角色:工厂方法模式所创建的任何对象都是这个角
色的实例,具体产品角色由一个具体Java 类实现。
工厂类的示意性源代码如下所示。可以看出,这个工厂方法创建了一个新的具体产品的实例并返还给调用者。
代码清单8:Creator 类的源代码
public class Creator
{
//静态工厂方法
public static Product factory()
{
return new ConcreteProduct();
}
}
抽象产品角色的主要目的是给所有的具体产品类提供一个共同的类型,在最简单的情况下,可以简化为一个标识接口。所谓标识接口,就是没有声明任何方法的空接口。
代码清单9:抽象角色Product 接口的源代码
public interface Product
{
}
具体产品类的示意性源代码如下。
代码清单10:具体产品角色ConcreteProduct 类的源代码
public class ConcreteProduct implements Product
{
public ConcreteProduct(){}
}
虽然在这个简单的示意性实现里面只给出了一个具体产品类,但是在实际应用中一般都会遇到多个具体产品类的情况。
五.简单工厂模式的实现
1.多层次的产品结构
在真实的系统中,产品可以形成复杂的等级结构,比如下图所示的树状结构上就有多个抽象产品类和具体产品类。
这个时候,简单工厂模式采取的是以不变应万变的策略,一律使用同一个工厂类。如下图所示。
图中从Factory 类到各个Product 类的虚线代表创建(依赖)关系;从Client 到其他类的联线是一般依赖关系。这样做的好处是设计简单,产品类的等级结构不会反映到工厂类中来,从而产品类的等级结构的变化也就不会影响到工厂类。但是这样做的缺点是,增加新的产品必将导致工厂类的修改。
2. 使用Java 接口或者Java 抽象类
如果模式所产生的具体产品类彼此之间没有共同的商业逻辑,那么抽象产品角色可以由一个Java 接口扮演;相反,如果这些具体产品类彼此之间确有共同的商业逻辑,那么这些公有的逻辑就应当移到抽象角色里面,这就意味着抽象角色应当由一个抽象类扮演。在一个类型的等级结构里面,共同的代码应当尽量向上移动,以达到共享的目的,如下图所示。