原型模式(clone?)
Prototype pattern refers to creating duplicate object while keeping performance in mind. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object.
參考:
- tutorialspoint | prototype_pattern
- 博客園 | 原型模式
- 博客園 | Java深拷貝與序列化
原型模式是通過復制已有對象來快速創建新對象的方法,它適用于創建那些實例化很慢的對象,比如數據庫連接對象,在創建好這樣的對象后,我們可以緩存一份,下次需要這種對象時,我們可以直接返回一個該對象的拷貝。
使用場景
- 需要大量相似對象時,如果在類中需要大量相似的對象,并且這些對象中有很多屬性都是一樣的,只有個別屬性需要定制時,可以使用原型模式,因為直接從內從中復制對象比new一個新對象的性能要高得多。
- 如果一個對象的實例化過程很耗時耗力,可以使用原型模式。
Java Cloneable接口
Java中提供了一個標記接口Cloneable
,類如果實現了這個接口就可以使用Object
類中定義的clone
方法
如果沒有實現Cloneable接口,直接調用
clone()
會拋出CloneNotSupportedException
Object clone()
會返回當前對象的一個淺拷貝
深拷貝和淺拷貝
根據不同的對象類型,拷貝的內容也各不相同:
- 基本數據類型,如int,char等,直接拷貝值
- 對于字符串,拷貝時只復制引用,當字符串的值改變時,會從字符串池中重新生成新的字符串,最終結果和拷貝值一樣
- 對于對象,拷貝時只復制引用,如果要復制值,需要使用深拷貝
public class Out implements Cloneable{private String outName;private In in;public Out(String outName) {this.outName = outName;}public void setIn(In in) {this.in = in;}@Overridepublic String toString() {return "Out{" +"outName='" + outName + '\'' +", in=" + in +'}';}@Overrideprotected Out clone() throws CloneNotSupportedException {return (Out) super.clone();}public static void main(String[] args) {Out out = new Out("out");In in = new In("in name");out.setIn(in);Out out1 = null;try {// in 是一個object類型,所以在調用clone()時只復制了in的引用out1 = out.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}assert out1 != null;// 改變out1.in的name也會改變out中in的nameout1.in.setName("out1 in");System.out.println(out);System.out.println(out1);}
}
/*
Out{outName='out', in=In{name='out1 in'}}
Out{outName='out', in=In{name='out1 in'}}
*/
深拷貝 DeepCopy
Java中實現深拷貝可以手動拷貝object類型的屬性,但如果這個類型中還有object類型,就會很麻煩。
@Override
protected DCOut clone() throws CloneNotSupportedException {DCOut copy = (DCOut) super.clone();In copyIn = (In) this.in.clone();copy.setIn(copyIn);return copy;
}
還可以使用Serializable
接口,通過序列化,將堆中的對象數據信息復制一份到堆外,再反序列化成新的克隆對象
import java.io.*;public class DeepClone implements Serializable {private Object obj;public DeepClone(Object obj){this.obj = obj;}public Object deepClone() {Object result = null;//序列化ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = null;try {oos = new ObjectOutputStream(baos);oos.writeObject(obj);} catch (IOException e) {e.printStackTrace();}// 反序列化ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = null;try {ois = new ObjectInputStream(bais);result = ois.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}return result;}
}
python中的深拷貝和淺拷貝
In [1]: import copyIn [2]: a = [i for i in range(10)]In [3]: b = copy.copy(a)In [4]: a is b
Out[4]: FalseIn [5]: c = [[1, 2], [3, 4]]In [6]: d = copy.copy(c)In [7]: d is c
Out[7]: FalseIn [8]: d[0] is c[0]
Out[8]: TrueIn [9]: e = copy.deepcopy(c)In [10]: e[0] is c[0]
Out[10]: FalseIn [11]:
python內置的copy模塊提供了深拷貝和淺拷貝的功能,python中淺拷貝只會拷貝父對象,不會拷貝父對象內部的子對象
python切片屬于淺拷貝
例
《大話設計模式》里簡歷的例子
package pers.junebao.prototype_pattern;import pers.junebao.prototype_pattern.deep_copy.DeepClone;import java.io.Serializable;public class Resume implements Cloneable, Serializable {private String name;private String education;private String sex;Resume(String name) {this.name = name;}public void setName(String name) {this.name = name;}public void setEducation(String education) {this.education = education;}public void setSex(String sex) {this.sex = sex;}public void print(){System.out.println("name: " + this.name);System.out.println("sex : " + this.sex);System.out.println("education: " + this.education);}@Overridepublic Resume clone() {Resume resume = null;// 深拷貝resume = (Resume) DeepClone.deepClone(this);return resume;}
}
package pers.junebao.prototype_pattern;public class Main {public static void main(String[] args) {Resume resume = new Resume("JuneBao");resume.setSex("男");resume.setEducation("本科");Resume resume1 = resume.clone();resume1.setSex("女");resume.print();resume1.print();}
}
GitHub | 完整代碼