原型模式(Prototype)
在有些系統中,存在大量相同或相似對象的創建問題,如果用傳統的構造函數來創建對象,會比較複雜且耗時耗資源,用原型模式生成對象就很高效,就像孫悟空拔下猴毛輕輕一吹就變出很多孫悟空一樣簡單。
原型模式的定義與特點
- 原型(Prototype)模式的定義如下:
用一個已經創建的實例作爲原型,通過複製該原型對象來創建一個和原型相同或相似的新對象。在這裏,原型實例指定了要創建的對象的種類。用這種方式創建對象非常高效,根本無須知道對象創建的細節。 - 原型(Prototype)模式的優點:
1.性能提高。
2.逃避構造函數的約束。 - 原型(Prototype)模式的缺點:
1.配備克隆方法需要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不一定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。
2.必須實現 Cloneable 接口。
原型模式的結構與實現
由於Java提供了對象的 clone() 方法,所以用 Java 實現原型模式很簡單。
- 模式的結構
原型模式包含以下主要角色。
1.抽象原型類: 規定了具體原型對象必須實現的接口。
2.具體原型類: 實現抽象原型類的 clone() 方法,它是可被複制的對象。
3.訪問類: 使用具體原型類中的 clone() 方法來複制新的對象。
其結構圖如圖 1 所示。
3. 模式的實現
原型模式的克隆分爲淺克隆和深克隆,Java 中的 Object 類提供了淺克隆的 clone() 方法,具體原型類只要實現 Cloneable 接口就可實現對象的淺克隆,這裏的 Cloneable 接口就是抽象原型類。其代碼如下:
//具體原型類
class Realizetype implements Cloneable
{
Realizetype()
{
System.out.println("具體原型創建成功!");
}
public Object clone() throws CloneNotSupportedException
{
System.out.println("具體原型複製成功!");
return (Realizetype)super.clone();
}
}
//原型模式的測試類
public class PrototypeTest
{
public static void main(String[] args)throws CloneNotSupportedException
{
Realizetype obj1=new Realizetype();
Realizetype obj2=(Realizetype)obj1.clone();
System.out.println("obj1==obj2?"+(obj1==obj2));
}
}
程序的運行結果如下:
具體原型創建成功!
具體原型複製成功!
obj1==obj2?false
原型模式的實例
我們將創建一個抽象類 Shape 和擴展了 Shape 類的實體類。下一步是定義類 ShapeCache,該類把 shape 對象存儲在一個 Hashtable 中,並在請求的時候返回它們的克隆。PrototypePatternDemo,我們的演示類使用 ShapeCache 類來獲取 Shape 對象。
1.創建一個實現了 Cloneable 接口的抽象類。Shape.java
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
2.創建擴展了上面抽象類的實體類。Rectangle.java、Square.java、Circle.java
public class Rectangle extends Shape {
public Rectangle(){
type = "Rectangle";
}
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square extends Shape {
public Square(){
type = "Square";
}
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle extends Shape {
public Circle(){
type = "Circle";
}
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
3.創建一個類,從數據庫獲取實體類,並把它們存儲在一個 Hashtable 中。ShapeCache.java
import java.util.Hashtable;
public class ShapeCache {
private static Hashtable<String, Shape> shapeMap
= new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
// 對每種形狀都運行數據庫查詢,並創建該形狀
// shapeMap.put(shapeKey, shape);
// 例如,我們要添加三種形狀
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(),circle);
Square square = new Square();
square.setId("2");
shapeMap.put(square.getId(),square);
Rectangle rectangle = new Rectangle();
rectangle.setId("3");
shapeMap.put(rectangle.getId(),rectangle);
}
}
4.PrototypePatternDemo 使用 ShapeCache 類來獲取存儲在 Hashtable 中的形狀的克隆。
public class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}
執行程序,輸出結果:
Shape : Circle
Shape : Square
Shape : Rectangle
原型模式的應用場景
- 對象之間相同或相似,即只是個別的幾個屬性不同的時候。
- 對象的創建過程比較麻煩,但複製比較簡單的時候。
原型模式的擴展
原型模式可擴展爲帶原型管理器的原型模式,它在原型模式的基礎上增加了一個原型管理器 PrototypeManager 類。該類用 HashMap 保存多個複製的原型,Client 類可以通過管理器的 get(String id) 方法從中獲取複製的原型。其結構圖如下圖所示。
用帶原型管理器的原型模式來生成包含“圓”和“正方形”等圖形的原型,並計算其面積。分析:本實例中由於存在不同的圖形類,例如,“圓”和“正方形”,它們計算面積的方法不一樣,所以需要用一個原型管理器來管理它們,下圖所示是其結構圖。
程序代碼如下:
import java.util.*;
interface Shape extends Cloneable
{
public Object clone(); //拷貝
public void countArea(); //計算面積
}
class Circle implements Shape
{
public Object clone()
{
Circle w=null;
try
{
w=(Circle)super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println("拷貝圓失敗!");
}
return w;
}
public void countArea()
{
int r=0;
System.out.print("這是一個圓,請輸入圓的半徑:");
Scanner input=new Scanner(System.in);
r=input.nextInt();
System.out.println("該圓的面積="+3.1415*r*r+"\n");
}
}
class Square implements Shape
{
public Object clone()
{
Square b=null;
try
{
b=(Square)super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println("拷貝正方形失敗!");
}
return b;
}
public void countArea()
{
int a=0;
System.out.print("這是一個正方形,請輸入它的邊長:");
Scanner input=new Scanner(System.in);
a=input.nextInt();
System.out.println("該正方形的面積="+a*a+"\n");
}
}
class ProtoTypeManager
{
private HashMap<String, Shape>ht=new HashMap<String,Shape>();
public ProtoTypeManager()
{
ht.put("Circle",new Circle());
ht.put("Square",new Square());
}
public void addshape(String key,Shape obj)
{
ht.put(key,obj);
}
public Shape getShape(String key)
{
Shape temp=ht.get(key);
return (Shape) temp.clone();
}
}
public class ProtoTypeShape
{
public static void main(String[] args)
{
ProtoTypeManager pm=new ProtoTypeManager();
Shape obj1=(Circle)pm.getShape("Circle");
obj1.countArea();
Shape obj2=(Shape)pm.getShape("Square");
obj2.countArea();
}
}
運行結果如下所示:
這是一個圓,請輸入圓的半徑:3
該圓的面積=28.2735
這是一個正方形,請輸入它的邊長:3
該正方形的面積=9