| |
研究和使用创立性模式的必要性
面向对象的设计的目的之一,就是把责任进行划分,以分派给不同的对象。我们推荐这种划分责任的作法, 是因为它和封装(encapsulation)和分派(delegation)的精神是相符合的。创立性模式把对象的创立过程封装起来,使得创立实例的责任与使用实例的责任分割开, 并由专门的模块分管实例的创立,而系统在宏观上不再依赖于对象创立过程的细节。
所有面向对象的语言都有固定的创立对象的办法。爪哇语的办法就是使用new操作符。比如
| stringbuffer s = new stringbuffer(1000); |
就创立了一个对象s,其类型是stringbuffer。使用new操作符的短处是事先必须明确知道要实例化的类是什么, 而且实例化的责任往往与使用实例的责任不加区分。使用创立性模式将类实例化,首先不必事先知道每次是要实例化哪一个类, 其次把实例化的责任与使用实例的责任分割开来,可以弥补直接使用new操作符的短处。
而工厂模式就是专门负责将大量有共同接口的类实例化,而且不必事先知道每次是要实例化哪一个类的模式。
工厂模式有几种形态
工厂模式有以下几种形态:
简单工厂(simple factory)模式
工厂方法(factory method)模式,又称多形性工厂(polymorphic factory)模式
抽象工厂(abstract factory)模式,又称工具箱(kit或toolkit)模式
介绍简单工厂模式
比如说,你有一个描述你的后花园的系统,在你的后花园里有各种的花,但还没有水果。你现在要往你的系统里引进一些新的类,用来描述下列的水果:
葡萄 grapes
草莓 strawberry
萍果 apple
花和水果最大的不同,就是水果最终是可以采摘食用的。那么,很自然的作法就是建立一个各种水果都适用的接口,这样一来这些水果类作为相似的数据类型就可以和你的系统的其余部分,如各种的花有所不同,易于区分。
 图1. grape, strawberry和apple是拥有共同接口fruitif的类。 |
package com.javapatterns.simplefactory;
public interface fruitif { void grow();
void harvest();
void plant();
string color = null; string name = null; } |
代码清单1. 接口fruitif的源代码。这个接口确定了水果类必备的方法:种植plant(),生长grow(), 以及收获harvest()。
package com.javapatterns.simplefactory;
public class apple implements fruitif {
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; }
private int treeage; } |
代码清单2. 类apple的源代码。萍果是多年生木本植物,因此具备树龄treeage性质。
package com.javapatterns.simplefactory;
public class grape implements fruitif { 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 getseedful() { return seedful; }
public void setseedful(boolean seedful) { this.seedful = seedful; }
private boolean seedful; } |
代码清单3. 类grape的源代码。葡萄分为有籽与无籽两种,因此具有seedful性质。
package com.javapatterns.simplefactory;
public class strawberry implements fruitif { 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); } } |
代码清单4. 类strawberry的源代码。
你作为小花果园的主人兼园丁,也是系统的一部分,自然要由一个合适的类来代表,这个类就是 fruitgardener类。这个类的结构请见下面的uml类图。
 图2. fruitgardener类图。 |
fruitgardener类会根据要求,创立出不同的水果类,比如萍果apple,葡萄grape或草莓strawberry的实例。而如果接到不合法的要求,fruitgardener类会给出例外badfruitexception。
 图3. badfruitexception类图。 |
package com.javapatterns.simplefactory;
public class fruitgardener { public fruitif 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"); } } } |
代码清单5. fruitgardener类的源代码。
package com.javapatterns.simplefactory;
public class badfruitexception extends exception { public badfruitexception(string msg) { super(msg); } } |
代码清单6. badfruitexception类的源代码。
在使用时,只须呼叫fruitgardener的factory()方法即可
try { fruitgardener gardener = new fruitgardener();
gardener.factory("grape"); gardener.factory("apple"); gardener.factory("strawberry"); ... } catch(badfruitexception e) { ... } |
就这样你的小果园一定会有百果丰收啦!
简单工厂模式的定义
总而言之,简单工厂模式就是由一个工厂类根据参数来决定创立出那一种产品类的实例。下面的uml类图就精确定义了简单工厂模式的结构。
 图4. 简单工厂模式定义的类图。 |
public class creator { public product factory() { return new concreteproduct(); }
}
public interface product { }
public class concreteproduct implements product { public concreteproduct(){}
} |
代码清单7. 简单工厂模式框架的源代码。
简单工厂模式实际上就是我们要在后面介绍的,工厂方法模式的一个简化了的情形。在读者熟悉了本节所介绍的简单工厂模式后,就不难掌握工厂方法模式了。
问答题
在本节开始时不是说,工厂模式就是在不使用new操作符的情况下,将......类实例化的吗, 可为什么在具体实现时,仍然使用了new操作符呢?
在本节的小果园系统里有三种水果类,可为什么在图3.(简单工厂模式定义的类图) 中产品(product)类只有一种呢?
请使用简单工厂模式设计一个创立不同几何形状,如圆形,方形和三角形实例的描图员(art tracer)系统。每个几何图形都要有画出draw()和擦去erase()两个方法。当描图员接到指令,要求创立不支持的几何图形时,要提出badshapeexception例外。
请简单举例说明描图员系统怎样使用。
在简单工厂模式的定义(见图4)中和花果园例子中,factory()方法都是属于实例的, 而非静态的或是类的方法。factory()方法可不可以是静态的方法呢?
问答题答案
对整个系统而言,工厂模式把具体使用new操作符的细节包装和隐藏起来。当然只要程序是用爪哇语言写的, 爪哇语言的特征在细节里一定会出现的。
图3.(简单工厂模式定义的类图),是精减后的框架性类图,用于给出这一模式的准确而精练的定义。产品(product)类到底会有几种,则要对每个系统作具体分析。
这里给出问题的完整答案。描图员(art tracer)系统的uml如下
点击查看大图 系统的源代码如下 |
package com.javapatterns.simplefactory.exercise;
public class arttracer { public shape factory(string which) throws badshapeexception { if (which.equalsignorecase("circle")) { return new circle(); } else if (which.equalsignorecase("square")) { return new square(); } else if (which.equalsignorecase("triangle")) { return new triangle(); } else { throw new badshapeexception(which); } } } |
代码清单8. arttracer类的源代码。
package com.javapatterns.simplefactory.exercise;
public interface shape { void draw();
void erase(); } |
代码清单9. shape接口的源代码。
package com.javapatterns.simplefactory.exercise;
public class square implements shape { public void draw() { system.out.println("square.draw()"); }
public void erase() { system.out.println("square.erase()"); } } |
代码清单10. square类的源代码。
package com.javapatterns.simplefactory.exercise;
public class circle implements shape { public void draw() { system.out.println("circle.draw()"); }
public void erase() { system.out.println("circle.erase()"); } } |
代码清单11. circle类的源代码。
package com.javapatterns.simplefactory.exercise;
public class triangle implements shape { public void draw() { system.out.println("triangle.draw()"); }
public void erase() { system.out.println("triangle.erase()"); } } |
代码清单12. triangle类的源代码。
package com.javapatterns.simplefactory.exercise;
public class badshapeexception extends exception { public badshapeexception(string msg) { super(msg); } } |
代码清单13. badshapeexception类的源代码。
描图员(art tracer)系统使用方法如下
try { arttracer art = new arttracer();
art.factory("circle"); art.factory("square"); art.factory("triangle");
art.factory("diamond"); } catch(badshapeexception e) { ... } |
注意对arttracer类提出菱形(diamond)请求时,会收到badshapeexception例外。
显然factory()可以是静态的或是类的方法。本文这样介绍简单工厂模式,是为了能方便与后面介绍的工厂方法模式作一比较。
|
|