Design Pattern: Prototype 模式
您从图书馆的期刊从发现了几篇您感兴趣的文章,由于这是图书馆的书,您不可以直接在书中作记号或写字,所以您将当中您所感兴趣的几个主题影印出来,这下子您就可在影印的文章上画记重点。
Prototype模式的作用有些类似上面的描述,您在父类别中定义一个clone()方法,而在子类别中重新定义它,当客户端对于所产生的物件有兴趣并想加以利用,而您又不想破坏原来的物件,您可以产生一个物件的复本给它。
Prototype具有展示的意味,就像是展览会上的原型车款,当您对某个车款感兴趣时,您可以购买相同款示的车,而不是车展上的车。
在软体设计上的例子会更清楚的说明为何要进行物件复制,假设您要设计一个室内设计软体,软体中有一个展示家具的工具列,您只要点选工具列就可以产生一个家具复本,例如一张椅子或桌子,您可以拖曳这个复制的物件至设计图中,随时改变它的位置、颜色等等,当您改变设计图中的物件时,工具列上的原型工具列是不会跟着一起改变的,这个道理是无需解释的。
下面的 UML 类别图表示了上述的简单概念:
protoType-1.jpg
Prototype模式的重点在于clone(),它负责复制物件本身并传回,但这个clone()本身在实作上存在一些困难,尤其是当物件本身又继承另一个物件时,如何确保复制的物件完整无误,在不同的程式语言中有不同的作法。
在Java中的作法是透过实作一个Cloneable介面,它只是一个声明的介面,并无规定任何实作的方法,您的目的是改写Object的clone ()方法,使其具备有复制物件的功能,这个方面建议您参考:How to avoid traps and correctly override methods from java.lang.Object。
用一个简单的例子来实作上图中的结构,这个例子利用了Java语言本身的clone特性:
AbstractFurniture.java
public abstract class AbstractFurniture implements Cloneable { public abstract void draw(); // 在Design Pattern上,以下的clone是抽象未实作的 // 实际上在Java中class都继承自Object // 所以在这边我们直接重新定义clone() // 这是为了符合Java现行的clone机制 protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
import java.awt.*;public class CircleTable extends AbstractFurniture { protected Point center; public void setCenter(Point center) { this.center = center; } protected Object clone () throws CloneNotSupportedException { Object o = super.clone(); if(this.center != null) { ((CircleTable) o).center = (Point) center.clone(); } return o; } public void draw() { System.out.println("\t圆桌\t中心:(" + center.getX() + ", " + center.getY()+ ")"); } }
import java.awt.*;public class SquareTable extends AbstractFurniture { protected Rectangle rectangle; public void setRectangle(Rectangle rectangle) { this.rectangle = rectangle; } protected Object clone () throws CloneNotSupportedException { Object o = super.clone(); if(this.rectangle != null) { ((SquareTable) o).rectangle = (Rectangle) rectangle.clone(); } return o; } public void draw() { System.out.print("\t方桌\t位置:(" + rectangle.getX() + ", " + rectangle.getY()+ ")"); System.out.println(" / 宽高:(" + rectangle.getWidth() + ", " + rectangle.getHeight()+ ")"); } }
import java.util.*;public class House { private Vector vector; public House() { vector = new Vector(); } public void addFurniture(AbstractFurniture furniture) { vector.addElement(furniture); System.out.println("现有家具...."); Enumeration enumeration = vector.elements(); while(enumeration.hasMoreElements()) { AbstractFurniture f = (AbstractFurniture) enumeration.nextElement(); f.draw(); } System.out.println(); } }
import java.awt.*;public class Application { private AbstractFurniture circleTablePrototype; public void setCircleTablePrototype( AbstractFurniture circleTablePrototype) { this.circleTablePrototype = circleTablePrototype; } public void runAppExample() throws Exception { House house = new House(); CircleTable circleTable = null; // 从工具列选择一个家具加入房子中 circleTable = (CircleTable) circleTablePrototype.clone(); circleTable.setCenter(new Point(10, 10)); house.addFurniture(circleTable); // 从工具列选择一个家具加入房子中 circleTable = (CircleTable) circleTablePrototype.clone(); circleTable.setCenter(new Point(20, 30)); house.addFurniture(circleTable); } public static void main(String[] args) throws Exception { Application application = new Application(); application.setCircleTablePrototype( new CircleTable()); application.runAppExample(); } }