1 模式簡介
原型模式的定義:通過複製一個現有的對象(原型)來得到一個相似的對象。
原型模式的UML圖如下圖所示:
從上圖中可以看到,所有的對象實體類都是繼承自一個Prototype的父類,而prototype類有一個可以複製對象的方法clone(),可以複製其任意子類。這樣,所有Prototype類的子類在整個程序中只需要new一次,其他情況下只需要從這幾個類中clone即可。
原型模式的適用場景:
1) 當系統中需要重複的使用某一些類的對象時;
2) 當這些對象的差別不是很大或完全相同時;
3) 當一個類的對象必須在某幾種狀態之中時;
4) 當系統需要對對象進行緩存時。
原型模式的優點:
1) 克隆一般是在內存中以流的形式進行的,比創建一個對象速度快,因此原型模式可以提高系統的性能;
2) 克隆對象的時候可以完全忽略類中的構造方法的訪問權限,因此逃避構造函數的約束。
原型模式的缺點:
1) 克隆並不是對所有的數據類型都適用,有些內部類(如ArrayList)是不支持克隆的;
2) 克隆會忽略構造函數的約束,因此對於一些單例的類也會生成克隆對象,容易導致系統錯誤。
2 案例代碼
需求:有三種形狀圓形(Circle)、矩形(Rectangle)和三角形(Triangle)。多次調用這些形狀排成一個隊列集合。
分析:我們可以把這個題目中的幾個邏輯想象成如下圖所示的關係:
我們讓圓形(Circle)、矩形(Rectangle)和三角形(Triangle)三種圖形繼承Shape父類,然後在緩存ShapeCache類中的HashMap中分別緩存三種形狀,當主函數中需要某種形狀的時候,我們就從緩存中克隆一個使用。
說明:
在JAVA中有一個Cloneable接口,是專門用來實現原型模式的。只需要讓一個類實現這個接口,重寫Object類的clone()方法,就可以方便的實現原型模式了。
代碼:
1) 原型父類Shape:
public class Shape implements Cloneable {
protected String type;
@Override
protected Object clone() {
Shape shape = null;
try {
shape = (Shape) super.clone();
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return shape;
}
public void introduce() {
System.out.println("This is a " + type);
}
}
2) Shape的子類(以Circle類爲例):
public class Circle extends Shape {
public Circle() {
super.type = "Circle";
}
}
3) 緩存類ShapeCache:
public class ShapeCache {
private Map<String, Shape> shapes;
public ShapeCache() {
loadCache();
}
private void loadCache() {
shapes = new HashMap<>();
shapes.put("circle", new Circle());
shapes.put("rectangle", new Rectangle());
shapes.put("triangle", new Triangle());
}
public Shape getShape(String id) {
Shape shape = shapes.get(id);
return (Shape) shape.clone();
}
}
4) 測試類Test:
public class Test {
public static void main(String[] args) {
List<Shape> shapeList = new ArrayList<>();
ShapeCachecache = new ShapeCache();
shapeList.add(cache.getShape("triangle"));
shapeList.add(cache.getShape("circle"));
shapeList.add(cache.getShape("rectangle"));
shapeList.add(cache.getShape("circle"));
shapeList.add(cache.getShape("rectangle"));
shapeList.add(cache.getShape("triangle"));
shapeList.add(cache.getShape("rectangle"));
shapeList.add(cache.getShape("circle"));
for (Shape shape : shapeList) {
shape.introduce();
}
}
}
5) 運行結果如下圖所示:
3 擴展
上面說過,JAVA中爲我們提供了一個Cloneable接口,只有實現了這個接口之後,重寫Object類中的clone()方法纔有效,否則會報CloneNotSupportedException異常。
使用原型模式複製對象時不會調用類的構造方法,因爲複製出來的對象是原型對象複製粘貼出來的,是直接在內存中複製數據,因此不會調用到類的構造方法,甚至連訪問權限都對原型模式無效,這一點在編程時需要特別注意。
Object類的clone()方法只能拷貝對象中的8種基本數據類型,對於數組、容器、引用對象等都不會拷貝,這種現象被成爲淺拷貝。與淺拷貝對應的是深拷貝,即在對對象進行淺拷貝之後,將對象中的數組、容器、引用對象單獨進行拷貝。
最後貼出原型模式的GitHub地址:【GitHub - Prototype】。