【設計模式 - 4】之原型模式(Prototype)


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】


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章