寫在前面
- 記錄學(xué)習(xí)設(shè)計模式的筆記
- 提高對設(shè)計模式的靈活運用
學(xué)習(xí)地址
https://www.bilibili.com/video/BV1G4411c7N4
https://www.bilibili.com/video/BV1Np4y1z7BU
參考文章
http://c.biancheng.net/view/1317.html
項目源碼
https://gitee.com/zhuang-kang/DesignPattern
6闹伪,原型模式
6.1原形模式的定義和特點
原型(Prototype)模式的定義如下:用一個已經(jīng)創(chuàng)建的實例作為原型夺颤,通過復(fù)制該原型對象來創(chuàng)建一個和原型相同或相似的新對象梯浪。在這里胰耗,原型實例指定了要創(chuàng)建的對象的種類。用這種方式創(chuàng)建對象非常高效五督,根本無須知道對象創(chuàng)建的細(xì)節(jié)绵载。例如,Windows 操作系統(tǒng)的安裝通常較耗時乘盖,如果復(fù)制就快了很多焰檩。
原型模式的優(yōu)點:
- Java自帶的原型模式基于內(nèi)存二進(jìn)制流的復(fù)制憔涉,在性能上比直接 new 一個對象更加優(yōu)良。
- 可以使用深克隆方式保存對象的狀態(tài)析苫,使用原型模式將對象復(fù)制一份兜叨,并將其狀態(tài)保存起來,簡化了創(chuàng)建對象的過程衩侥,以便在需要的時候使用(例如恢復(fù)到歷史某一狀態(tài))国旷,可輔助實現(xiàn)撤銷操作。
原型模式的缺點:
- 需要為每一個類都配置一個 clone 方法
- clone 方法位于類的內(nèi)部茫死,當(dāng)對已有類進(jìn)行改造的時候跪但,需要修改代碼,違背了開閉原則峦萎。
- 當(dāng)實現(xiàn)深克隆時屡久,需要編寫較為復(fù)雜的代碼,而且當(dāng)對象之間存在多重嵌套引用時爱榔,為了實現(xiàn)深克隆被环,每一層對象對應(yīng)的類都必須支持深克隆,實現(xiàn)起來會比較麻煩详幽。因此筛欢,深克隆、淺克隆需要運用得當(dāng)唇聘。
6.2 原型模式的結(jié)構(gòu)與實現(xiàn)
6.2.1 原形模式的結(jié)構(gòu)
原型模式包含以下主要角色版姑。
- 抽象原型類:規(guī)定了具體原型對象必須實現(xiàn)的接口。
- 具體原型類:實現(xiàn)抽象原型類的 clone() 方法迟郎,它是可被復(fù)制的對象漠酿。
- 訪問類:使用具體原型類中的 clone() 方法來復(fù)制新的對象。
6.2.2 代碼實現(xiàn)
淺克禄涯丁:創(chuàng)建一個新對象炒嘲,新對象的屬性和原來對象完全相同宇姚,對于非基本類型屬性,仍指向原有屬性所指向的對象的內(nèi)存地址夫凸。
深克禄肜汀:創(chuàng)建一個新對象,屬性中引用的其他對象也會被克隆夭拌,不再指向原有對象地址魔熏。
6.2.2.1 淺拷貝
IdCard
package com.zhuang.prototype.shallowclone;
/**
* @Classname IdCard
* @Description 淺拷貝的示例
* @Date 2021/3/19 12:16
* @Created by dell
*/
public class IdCard {
private String id;
public IdCard(String id) {
this.id = id;
}
@Override
public String toString() {
return "IdCard{" +
"id=" + id +
'}';
}
}
Person
package com.zhuang.prototype.shallowclone;
/**
* @Classname Person
* @Description 淺拷貝的示例
* @Date 2021/3/19 12:17
* @Created by dell
*/
public class Person implements Cloneable {
private String name;
private int age;
private IdCard idCard;
public Person() {
}
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard + ", idCard.hashCode=" + idCard.hashCode() +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
PersonTest
package com.zhuang.prototype.shallowclone;
/**
* @Classname PersonTest
* @Description 淺拷貝測試類
* @Date 2021/3/19 12:17
* @Created by dell
*/
public class PersonTest {
public static void main(String[] args) throws Exception {
Person person = new Person("張三", 20, new IdCard("10086"));
Person person1 = (Person) person.clone();
Person person2 = (Person) person.clone();
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
我們發(fā)現(xiàn)可以通過實現(xiàn)implements Cloneable
來完成淺拷貝,基本變量是值傳遞克隆鸽扁,而引用對象IdCard
則是引用傳遞蒜绽,這不符合我們面向?qū)ο笏枷耄恳粋€Person
應(yīng)該都有一個獨立的IdCard
桶现,而不是共用一個躲雅,而要解決這種問題,我們需要使用深克隆
6.2.2.2 深拷貝(第一種)
IdCard
package com.zhuang.prototype.deepclone.one;
/**
* @Classname IdCard
* @Description 深克隆的示例
* @Date 2021/3/19 12:33
* @Created by dell
*/
//實現(xiàn)Cloneable接口
public class IdCard implements Cloneable {
private String id;
public IdCard(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "IdCard{" +
"id='" + id + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Person
package com.zhuang.prototype.deepclone.one;
/**
* @Classname Person
* @Description 深克隆的示例
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class Person implements Cloneable {
private String name;
private int age;
private IdCard idCard;
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "Person{" +
"personHashCode=" + this.hashCode() +
", name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard +
", idCardHashCode=" + idCard.hashCode() +
'}';
}
//深克隆需要自己手動實現(xiàn)骡和,在對象引用中也要實現(xiàn)clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
//完成基本數(shù)據(jù)類型的拷貝
//通過new關(guān)鍵字創(chuàng)建的對象是引用類型
Object person = super.clone();
//對引用類型單獨處理
Person p = (Person) person;
IdCard idCard = (IdCard) p.getIdCard().clone(); //實現(xiàn)自己的克隆
p.setIdCard(idCard);
return p;
}
}
PersonTest
package com.zhuang.prototype.deepclone.one;
import java.io.Serializable;
/**
* @Classname PersonTest
* @Description 深克隆測試類
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class PersonTest implements Serializable {
public static void main(String[] args) throws Exception {
Person person = new Person("張三", 20, new IdCard("10086"));
Person person1 = (Person) person.clone();
Person person2 = (Person) person.clone();
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
使用這種深克隆的方式相赁,完美的解決了當(dāng)數(shù)據(jù)類型為引用類型時,只是拷貝原引用對象地址而不是一個全新的引用對象的引用慰于,但是這種實現(xiàn)有一個很大的弊端钮科,需要在每一個對象中都實現(xiàn)clone方法,如果類全是你自己寫的婆赠,那自然沒問題绵脯,實現(xiàn)一下就行了,不過有點麻煩休里。但是蛆挫,如果你引用的是第三方的一個類,無法修改源代碼份帐,這種方式璃吧,顯然就無法實現(xiàn)深克隆了
6.2.2.2 深拷貝(第二種)
IdCard
package com.zhuang.prototype.deepclone.two;
/**
* @Classname IdCard
* @Description 深克隆的示例2
* @Date 2021/3/19 12:33
* @Created by dell
*/
//實現(xiàn)Serializable接口
public class IdCard implements Serializable {
private String id;
public IdCard(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "IdCard{" +
"id='" + id + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Person
package com.zhuang.prototype.deepclone.two;
import java.io.*;
/**
* @Classname Person
* @Description 深克隆的示例2
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class Person implements Serializable {
private String name;
private int age;
private IdCard idCard;
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "Person{" +
"personHashCode=" + this.hashCode() +
", name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard +
", idCardHashCode=" + idCard.hashCode() +
'}';
}
//序列化的方式
public Person deelClone() {
//創(chuàng)建流對象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (Person) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
ois.close();
bis.close();
oos.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
PersonTest
package com.zhuang.prototype.deepclone.two;
/**
* @Classname PersonTest
* @Description 深克隆測試類
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class PersonTest {
public static void main(String[] args) throws Exception {
//創(chuàng)建一個對象
Person person = new Person("張三", 20, new IdCard("10086"));
//克隆兩個對象
Person person1 = (Person) person.deelClone();
Person person2 = (Person) person.deelClone();
System.out.println("深拷貝(第二種 實現(xiàn)序列化接口)");
//打印三人信息
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
這種方式我們需要手動編寫deepClone方法,使用Java流中的序列化與反序列化來實現(xiàn)深克隆废境,但是這種實現(xiàn)畜挨,需要在每一個類中都繼承序列化Serializable接口,這種方式噩凹,如果你調(diào)用的是第三方類巴元,也有可能第三方類上沒有實現(xiàn)Serializable序列化接口,但是一般來說驮宴,大多都會實現(xiàn)逮刨,總的來說,這種比較推薦使用,而且效率也高
6.3 原型模式的應(yīng)用場景
- 對象之間相同或相似修己,即只是個別的幾個屬性不同的時候恢总。
- 創(chuàng)建對象成本較大,例如初始化時間長睬愤,占用CPU太多片仿,或者占用網(wǎng)絡(luò)資源太多等,需要優(yōu)化資源尤辱。
- 創(chuàng)建一個對象需要繁瑣的數(shù)據(jù)準(zhǔn)備或訪問權(quán)限等砂豌,需要提高性能或者提高安全性。
- 系統(tǒng)中大量使用該類對象光督,且各個調(diào)用者都需要給它的屬性重新賦值阳距。
- 在 Spring中,原型模式應(yīng)用的非常廣泛结借,例如 scope='prototype'筐摘、JSON.parseObject() 等都是原型模式的具體應(yīng)用。
6.4 原型模式的注意事項和細(xì)節(jié)
- 創(chuàng)建新的對象比較復(fù)雜時映跟,可以利用原型模式簡化對象的創(chuàng)建過程蓄拣,同時也能提高效率
- 不用重新初始化對象扬虚,動態(tài)地獲得對象運行時的狀態(tài)
- 如果原始對象發(fā)生變化(增加或減少屬性)努隙,其他克隆對象也會發(fā)生相應(yīng)的變化,無需修改代碼
寫在最后
- 如果我的文章對你有用辜昵,請給我點個??荸镊,感謝你??!
- 有問題堪置,歡迎在評論區(qū)指出躬存!??