你发现当你添加新类型到一个系统中去时,第一感是应用多态创建一个共同的接口对那些新的类型。这分离了你系统中代码不变的部分从你添加的特定类型。新类型的添加也许不会干扰到存在的编码…或者看起来那样。首先,它仅仅需要出现在你需要改变编码的地方,就是在这样的设计中你需要继承一个新类型的地方,但是这并不完全这样。你必须仍然创建新类型的对象,并且在创建这点上,你必须创建专门的构造函数。这样,如果你创建对象的编码遍布于你的应用中,当添加新类型时,你还是会有同样的问题---在类型有关的地方,你仍然必须找出所有代码点。他发生在有关于这个案例的类型创建而非类型的应用(被多态顾及),但是结果是一样的:添加新类型会引发问题。
这个方案驱使对象通过一个共同的工厂(factory)来创建而非是创建编码遍布于你的系统中。如果所有你的程序代码无论是否需要都通过工厂来创建对象,那么你需要做的是当添加一个对象时修改你的工厂。
因此每个面向对象的程序创建对象,并且很可能你将添加新类型来扩展你的程序,工厂模式也许是最广泛应用的一个设计模式。
尽管只有简单工厂是一个真正的单态,你将发现每个专门的工厂类在更一般的工厂类型中仅有一个单个的实例。
作为一个简单例子,让我们回顾一下Shape那个系统。
一种方式是让工厂成为基类的一个静态方法。
//: factory:shapefact1:ShapeFactory1.java
// A simple static factory method.
package factory.shapefact1;
import java.util.*;
import junit.framework.*;
abstract class Shape {
public abstract void draw();
public abstract void erase();
public static Shape factory(String type) {
if (type.equals("Circle"))
return new Circle();
if (type.equals("Square"))
return new Square();
throw new RuntimeException("Bad shape creation: " + type);
}
}
class Circle extends Shape {
Circle() {
} // Package-access constructor
public void draw() {
System.out.println("Circle.draw");
}
public void erase() {
System.out.println("Circle.erase");
}
}
class Square extends Shape {
Square() {
} // Package-access constructor
public void draw() {
System.out.println("Square.draw");
}
public void erase() {
System.out.println("Square.erase");
}
}
public class ShapeFactory1 extends TestCase {
String shlist[] = { "Circle", "Square", "Square", "Circle", "Circle",
"Square" };
List shapes = new ArrayList();
public void test() {
Iterator it = Arrays.asList(shlist).iterator();
while (it.hasNext())
shapes.add(Shape.factory((String) it.next()));
it = shapes.iterator();
while (it.hasNext()) {
Shape s = (Shape) it.next();
s.draw();
s.erase();
}
}
public static void main(String args[]) {
junit.textui.TestRunner.run(ShapeFactory1.class);
}
} // /:~
方法factory() 获取一个参数来允许他决定创建何种类型的shape,在这个例子中参数为String类型,它可以为任何数据集。方法factory()仅仅是系统中需要改变的另外一段编码,当一个新shape被添加的时候(对象初始化数据推测来自于系统外部,并且如同上面的一个例子不是一个硬编码排列)。
鼓励创建仅仅发生在factory()方法中,专门类型shape的构造函数可以通过包路径访问,因此factory()方法已经访问构造函数除非他没有在包的外部不可用。
前面例子中的静态工厂方法迫使创建操作集中在一个点上,因此这是你需要改变编码的唯一地方。这当然是一个合理的方案,它提供了一种封箱围绕创建对象的过程。然而,《设计模式》一书强调的工厂方法模式的理由,是如此不同类型的工厂能够从基本的工厂子类化(上面的设计被作为一个特例)并且这本书并未提供一个例子,而是仅仅重复抽象工厂的那个例子(下面一节中你会看到)。这就是修改后的ShapeFactory1.java,因此工厂方法作为虚拟的功能是在分离的类中。注意:特别的Shape类在需要时动态的加载:
//: factory:shapefact2:ShapeFactory2.java
// Polymorphic factory methods.
package factory.shapefact2;
import java.util.*;
import junit.framework.*;
interface Shape {
void draw();
void erase();
}
abstract class ShapeFactory {
protected abstract Shape create();
private static Map factories = new HashMap();
public static void addFactory(String id, ShapeFactory f) {
factories.put(id, f);
}
// A Template Method:
public static final Shape createShape(String id) {
if (!factories.containsKey(id)) {
try {
// Load dynamically
Class.forName("factory.shapefact2." + id);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Bad shape creation: " + id);
}
// See if it was put in:
if (!factories.containsKey(id))
throw new RuntimeException("Bad shape creation: " + id);
}
return ((ShapeFactory) factories.get(id)).create();
}
}
class Circle implements Shape {
private Circle() {
}
public void draw() {
System.out.println("Circle.draw");
}
public void erase() {
System.out.println("Circle.erase");
}
private static class Factory extends ShapeFactory {
protected Shape create() {
return new Circle();
}
}
static {
ShapeFactory.addFactory("Circle", new Factory());
}
}
class Square implements Shape {
private Square() {
}
public void draw() {
System.out.println("Square.draw");
}
public void erase() {
System.out.println("Square.erase");
}
private static class Factory extends ShapeFactory {
protected Shape create() {
return new Square();
}
}
static {
ShapeFactory.addFactory("Square", new Factory());
}
}
public class ShapeFactory2 extends TestCase {
String shlist[] = { "Circle", "Square", "Square", "Circle", "Circle",
"Square" };
List shapes = new ArrayList();
public void test() {
// This just makes sure it will complete
// without throwing an exception.
Iterator it = Arrays.asList(shlist).iterator();
while (it.hasNext())
shapes.add(ShapeFactory.createShape((String) it.next()));
it = shapes.iterator();
while (it.hasNext()) {
Shape s = (Shape) it.next();
s.draw();
s.erase();
}
}
public static void main(String args[]) {
junit.textui.TestRunner.run(ShapeFactory2.class);
}
} // /:~
现在,工厂方法出现在它自己的类中,ShapeFactory,正如create()方法。这是一个protected方法,意味着它不能直接被调用,但是能够被重写。Shape的每个子类必须创建它们自己的ShapeFactory子类,并且重写create()方法去创建一个它们自己类型的对象。实际形状的创建通过调用ShapeFactory.createShape( )来执行,它是一个静态方法在ShapeFactory中使用Map找到合适的基于标示符的你所传递的工厂对象。这工厂立刻被用来创建形状对象,但是你能够想象一个更复杂的问题,在那合适的工厂对象被返回并且被调用者应用去创建一个对象在一个更复杂的方式。然而,看起来大多数时间你不需要多态工厂方法的复杂,并且基类的一个单一的静态方法(如ShapeFactory1.java中)将工作的很好。
注意,ShapeFactory必须通过加载含有工厂对象的Map来初始化,这发生在每个Shape实现的静态初始化子句中。因此添加一个新类型到这个设计中你必须继承这个类型,创建一个工厂,并且添加静态初始化子句将对象加载到Map中。这额外的复杂再一次建议静态工厂方法的应用如果你不需要创建个别的工厂对象。
抽象工厂(Abstract factories)
抽象工厂模式看起来和我们前面见到的工厂对象有些相似,它有几个工厂方法。每个工厂方法创建不同种类的对象。思想在于,在创建工厂对象这点,你决定所有的这些对象怎样被使用的那个工厂创建。《设计模式》中给出的例子轻量级访问各种GUI:你为你工作的GUI创建了一个合适的工厂对象,然后你请求菜单,按钮,等,它将自动的为GUI创建一个合适版本的条款。这样,你能够分离,在一个地方,从一个GUI到另外一个GUI的变化的影响。
作为另一个例子,假设你正创建一个一般目的游戏环境,并且你想能够支持不同类型的游戏。下面就是一个使用抽象工厂的例子:
//: factory:Games.java
// An example of the Abstract Factory pattern.
package factory;
import junit.framework.*;
interface Obstacle {
void action();
}
interface Player {
void interactWith(Obstacle o);
}
class Kitty implements Player {
public void interactWith(Obstacle ob) {
System.out.print("Kitty has encountered a ");
ob.action();
}
}
class KungFuGuy implements Player {
public void interactWith(Obstacle ob) {
System.out.print("KungFuGuy now battles a ");
ob.action();
}
}
class Puzzle implements Obstacle {
public void action() {
System.out.println("Puzzle");
}
}
class NastyWeapon implements Obstacle {
public void action() {
System.out.println("NastyWeapon");
}
}
// The Abstract Factory:
interface GameElementFactory {
Player makePlayer();
Obstacle makeObstacle();
}
// Concrete factories:
class KittiesAndPuzzles
implements GameElementFactory {
public Player makePlayer() {
return new Kitty();
}
public Obstacle makeObstacle() {
return new Puzzle();
}
}
class KillAndDismember
implements GameElementFactory {
public Player makePlayer() {
return new KungFuGuy();
}
public Obstacle makeObstacle() {
return new NastyWeapon();
}
}
class GameEnvironment {
private GameElementFactory gef;
private Player p;
private Obstacle ob;
public GameEnvironment(
GameElementFactory factory) {
gef = factory;
p = factory.makePlayer();
ob = factory.makeObstacle();
}
public void play() { p.interactWith(ob); }
}
public class Games extends TestCase {
GameElementFactory
kp = new KittiesAndPuzzles(),
kd = new KillAndDismember();
GameEnvironment
g1 = new GameEnvironment(kp),
g2 = new GameEnvironment(kd);
// These just ensure no exceptions are thrown:
public void test1() { g1.play(); }
public void test2() { g2.play(); }
public static void main(String args[]) {
junit.textui.TestRunner.run(Games.class);
}
} ///:~
在这个环境中,Player对象与Obstacle对象交互,但是依据你所玩的游戏种类有不同的游戏玩家和障碍。你决定游戏类型通过选择一个特定的GameElementFactory,GameEnvironment类控制建立和进行游戏。在这个例子中,建立和玩游戏是简单的,但是那些活动(条件初始和改变状态)决定游戏的展现。这里,GameEnvironment类没有设计成继承,尽管它也能那样做。
这也包含了两路分发(Double Dispatching)和工厂方法的例子,随后它们都会被展述。
1.添加Triangle类到ShapeFactory1.java文件中去。
2.添加Triangle类到ShapeFactory2.java文件中去
3.添加一个GameEnvironment的新类型GnomesAndFairies 类到Games.java 文件中去。
4. 修改ShapeFactory2.java,它使用一个抽象工厂来创建不同的形状集(例如,一个特定类型的工厂对象创建“thick shapes”,另外一个创建“thin shapes”,但是每一个工厂对象能够创建所有的Shapes:circles, squares, triangles etc.)
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/25966/viewspace-53317/,如需转载,请注明出处,否则将追究法律责任。