概述:
原型模式是創建型模式的一種,其特點在於通過“複製”一個已經存在的實例來返回新的實例,而不是新建實例。被複制的實例就是我們所稱的“原型”,這個原型是可定製的。原型模式多用於創建複雜的或者耗時的實例,因爲這種情況下,複製一個已經存在的實例使程序運行更高效;或者創建值相等,只是命名不一樣的同類數據。
應用場景:
- 資源優化場景,類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等。
- 性能和安全要求的場景,通過new產生一個對象需要非常繁瑣的數據準備或訪問權限,則可以使用原型模式。
- 一個對象多個修改者的場景,一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用。
- 在實際項目中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,通過clone的方法創建一個對象,然後由工廠方法提供給調用者。原型模式已經與Java融爲一體,大家可以隨手拿來使用。
複製原型分爲淺克隆與深克隆:
淺克隆:當原型對象被複制時,只複製它本身和其中包含的值類型的成員變量,而引用類型的成員變量並沒有複製。因此兩個對象的interest公共同一個內存地址,一個對象變化,會引起另一個對象響應的變化。
深克隆:除了對象本身被複制外,對象所包含的所有成員變量也將被複制。
下邊用測試代碼來具體闡述兩者的區別
角色分配:
Client:客戶端
Prototype:原型模式中的核心,用於創建原型對象,其實現了Cloneable接口,重寫了clone方法,並將訪問權限改爲public
ConcretePrototype:具體的需要創建的對象的類
測試代碼:
/**
* @Description 原型模式:實現Cloneable接口,重寫Object的clone方法
* @date 2019/10/21 16:33
* @Version V1.0
*/
@Data
public class Prototype implements Cloneable {
private String name;
private String context;
private String title;
private String address;
public Prototype(String name, String context, String title, String address) {
this.name = name;
this.context = context;
this.title = title;
this.address = address;
}
/**
* 克隆方法
*/
@Override
protected Prototype clone() throws CloneNotSupportedException{
return (Prototype) super.clone();
}
}
淺克隆:
/**
* @ClassName Thing
* @Description 淺拷貝 (1)JVM做了一個偷懶的拷貝動作,Object類提供的方法clone只是拷貝本對象,其對象內部的數組、引用對象等都不拷貝,還是指向原生對象的內部元素地址,這種拷貝就叫做淺拷貝
* (2)非常不安全
* @date 2019/10/21 16:45
* @Version V1.0
*/
@Data
public class Thing implements Cloneable {
private List<String> list = new ArrayList<String>();
@Override
protected Thing clone() throws CloneNotSupportedException{
return (Thing) super.clone();
}
}
深克隆:
/**
* @ClassName Thing2
* @Description 深拷貝:除了對象本身被複制外,對象所包含的所有成員變量也將被複制
* @date 2019/10/21 16:50
* @Version V1.0
*/
@Data
public class Thing2 implements Cloneable {
private ArrayList<String> list=new ArrayList<String>();
@Override
protected Thing2 clone() throws CloneNotSupportedException {
Thing2 thing2=null;
thing2=(Thing2) super.clone();
thing2.list=(ArrayList<String>) this.list.clone();
return thing2;
}
}
測試:
/**
* @ClassName ClientTest
* @Description TODO
* @date 2019/10/21 16:52
* @Version V1.0
*/
public class ClientTest {
public static void main(String[] args) throws CloneNotSupportedException {
test01();
test02();
test03();
}
/**
* 原型模式:模板測試
*/
public static void test01() throws CloneNotSupportedException{
Prototype mail=new Prototype("sxf", "go smx", "emailtosxf", "[email protected]");//ClientTest.main()com.yeepay.sxf.template8.Mail@2a5330
System.out.println("ClientTest.main()"+mail.toString());
Prototype mail2=mail.clone();
System.out.println("ClientTest.main()"+mail2.toString());//ClientTest.main()com.yeepay.sxf.template8.Mail@18872380
}
/**
* 原型模式:淺拷貝
*/
public static void test02() throws CloneNotSupportedException{
Thing thing1=new Thing();
thing1.setList("小李");
Thing thing2=thing1.clone();
thing1.setList("小張");
List<String> t=thing1.getList();
List<String> t2=thing2.getList();
System.out.println("-------------淺克隆開始--------------");
for (int i = 0; i < t.size(); i++) {
System.out.println("ClientTest.test02(t==>)"+t.get(i));
}
for (int i = 0; i < t2.size(); i++) {
System.out.println("ClientTest.test02(t2==>)"+t2.get(i));
}
System.out.println("-------------淺克隆結束--------------");
}
/**
* 原型模式:深拷貝
*/
public static void test03() throws CloneNotSupportedException{
Thing2 thing2a=new Thing2();
thing2a.setList("小李");
Thing2 thing2b=thing2a.clone();
thing2a.setList("小張");
List<String> t=thing2a.getList();
List<String> t2=thing2b.getList();
System.out.println("-------------深克隆開始--------------");
for (int i = 0; i < t.size(); i++) {
System.out.println("ClientTest.test02(t==>)"+t.get(i));
}
for (int i = 0; i < t2.size(); i++) {
System.out.println("ClientTest.test02(t2==>)"+t2.get(i));
}
System.out.println("-------------深克隆結束--------------");
}
}
測試結果:
原型設計模式中淺拷貝是將數組和引用對象的指針clone過來了,而深拷貝是將引用對象內部重新分配了內存地址,指針發生了變化。
要使用clone方法,在類的成員變量上就不要增加final關鍵字。