原型模式
用原型實例指定創(chuàng)建對象的,拷貝這些對象生成新的對象進行使用。
也可以直接進行new一個對象,但是當對象的構(gòu)造復雜時,new的效率會很低,使用clone更好
new適用于簡單的構(gòu)造
clone適用于復雜的構(gòu)造
應(yīng)用場景
- 資源消耗大的對象
- 節(jié)省資源,比如在for循環(huán)內(nèi)創(chuàng)建相同的對象
- 一個對象要讓其他人使用,并且使用過程中要改變這個對象的某些屬性,可考慮將原有對象拷貝,并且修改需要改變的屬性,在進行使用
- 對象結(jié)構(gòu)復雜的情況使用拷貝
淺拷貝代碼實現(xiàn)
<font color=#0099ff size=3 face="黑體">原型對象實現(xiàn)Cloneable接口</font>(默認是淺拷貝)
class Prototype implements Cloneable {
public Prototype clone() {
Prototype prototype = null;
try {
prototype = (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return prototype;
}
}
原型對象具體的內(nèi)容實現(xiàn)
class ConcretePrototype extends Prototype {
public String name;
public ArrayList<String> list = new ArrayList<>();
public ConcretePrototype() {
System.out.println("執(zhí)行了ConcretePrototype構(gòu)造函數(shù)");
}
@Override
public String toString() {
return "ConcretePrototype{" +
"name='" + name + '\'' +
", list=" + list +
'}';
}
public void show() {
System.out.println("原型模式實現(xiàn)類");
}
}
使用方式
ConcretePrototype cp = new ConcretePrototype();
cp.name = "原始數(shù)據(jù)";
for (int i = 0; i < 10; i++) {
cp.list.add(i, String.valueOf(i));//填充數(shù)據(jù)
}
ConcretePrototype cloneConcretePrototype = (ConcretePrototype) cp.clone();//實現(xiàn)拷貝
cloneConcretePrototype.name = "拷貝數(shù)據(jù)";
System.out.println(cp.toString());
System.out.println(cloneConcretePrototype.toString());
顯示結(jié)果:
執(zhí)行了ConcretePrototype構(gòu)造函數(shù)
ConcretePrototype{name='原始數(shù)據(jù)', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype{name='拷貝數(shù)據(jù)', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
<font color=#0099ff size=3 face="黑體">原型對象不實現(xiàn)Cloneable接口</font>(默認是淺拷貝)
其實就是把cloneable接口的內(nèi)容手動實現(xiàn)一次
class ConcretePrototype2 {
public String name;
public ArrayList<String> list = new ArrayList<>();
public ConcretePrototype2() {
System.out.println("執(zhí)行了ConcretePrototype2構(gòu)造函數(shù)");
}
public ConcretePrototype2 clone() {
//手動new對象進行賦值
concretePrototype2 concretePrototype2 = new ConcretePrototype2();
concretePrototype2.name = this.name;
concretePrototype2.list = this.list;
return concretePrototype2;
}
@Override
public String toString() {
return "ConcretePrototype2{" +
"name='" + name + '\'' +
", list=" + list +
'}';
}
public void show() {
System.out.println("原型模式實現(xiàn)類");
}
}
調(diào)用方式
ConcretePrototype2 cp = new ConcretePrototype2();
cp.name = "原始數(shù)據(jù)";
for (int i = 0; i < 10; i++) {
cp.list.add(i, String.valueOf(i));//填充數(shù)據(jù)
}
ConcretePrototype2 cloneConcretePrototype = (ConcretePrototype2) cp.clone();//實現(xiàn)拷貝
cloneConcretePrototype.name = "拷貝數(shù)據(jù)";
System.out.println(cp.toString());
System.out.println(cloneConcretePrototype.toString());
顯示結(jié)果
執(zhí)行了ConcretePrototype2構(gòu)造函數(shù)
執(zhí)行了ConcretePrototype2構(gòu)造函數(shù)
ConcretePrototype2{name='原始數(shù)據(jù)', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype2{name='拷貝數(shù)據(jù)', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
構(gòu)造函數(shù)執(zhí)行了兩次,說明了cloneable接口的復制是不會執(zhí)行2次構(gòu)造的,直接new對象則會調(diào)用2次構(gòu)造。
測試
修改復制后的數(shù)據(jù)
ConcretePrototype2 cp = new ConcretePrototype2();
cp.name = "原始數(shù)據(jù)";
for (int i = 0; i < 10; i++) {
cp.list.add(i, String.valueOf(i));//填充數(shù)據(jù)
}
ConcretePrototype2 cloneConcretePrototype = (ConcretePrototype2) cp.clone();//實現(xiàn)拷貝
cloneConcretePrototype.name = "拷貝數(shù)據(jù)";
cloneConcretePrototype.list.add("追加測試數(shù)據(jù)");
System.out.println(cp.toString());
System.out.println(cloneConcretePrototype.toString());
顯示結(jié)果
執(zhí)行了ConcretePrototype2構(gòu)造函數(shù)
執(zhí)行了ConcretePrototype2構(gòu)造函數(shù)
ConcretePrototype2{name='原始數(shù)據(jù)', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加測試數(shù)據(jù)]}
ConcretePrototype2{name='拷貝數(shù)據(jù)', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加測試數(shù)據(jù)]}
【追加測試數(shù)據(jù)無論是原型還是拷貝后的對象都有這個數(shù)據(jù)】
問題原因:深拷貝-淺拷貝
Cloneable接口默認實現(xiàn)的是淺拷貝,對于基本類型會進行拷貝,但是對于引用類型(集合,數(shù)據(jù),對象)等clone僅僅代表指向了同一個內(nèi)存地址,所以修改一個兩個都會變化。
解決方案:在引用類型的具體數(shù)據(jù)類型在進行一次clone,將淺拷貝處理為深拷貝
深拷貝代碼實現(xiàn)
代碼如下
public ConcretePrototype2 clone() {
//手動new對象進行賦值
// ConcretePrototype2 concretePrototype2 = new ConcretePrototype2();
// concretePrototype2.name = this.name;
// concretePrototype2.list = this.list;
// return concretePrototype2;
//將具體的集合修改為深拷貝
ConcretePrototype2 concretePrototype2 = new ConcretePrototype2();
concretePrototype2.name = this.name;
concretePrototype2.list = (ArrayList<String>) this.list.clone();
return concretePrototype2;
}
顯示結(jié)果
執(zhí)行了ConcretePrototype2構(gòu)造函數(shù)
執(zhí)行了ConcretePrototype2構(gòu)造函數(shù)
ConcretePrototype2{name='原始數(shù)據(jù)', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
ConcretePrototype2{name='拷貝數(shù)據(jù)', list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 追加測試數(shù)據(jù)]}
應(yīng)證我們的想法,對于集合,數(shù)組,對象等,如果要新增數(shù)據(jù),需要對其重寫更改具體的數(shù)據(jù)類型。
總結(jié)
原型模式可以避免構(gòu)造復雜的對象時的資源消耗問題,提升創(chuàng)建對象的效率。
可以保護原始對象的數(shù)據(jù)安全性
clone是二進制流,對于復雜的構(gòu)造對象,性能提升明顯,但是對于簡單的構(gòu)造沒必要使用clone