原型模式是一種創建型的模式,原型模式的簡單程度僅次於單例模式和迭代器模式,正是由於簡單,使用的場景非常多。
通過原型模式的通用類圖可以看到,其實原型模式的核心就是一個clone方法,通過該方法實現對象的拷貝。
原型模式的優點:
1.原型模式是在內存二進制流的拷貝,要比直接new一個對象性能好很多,特別是要在一個循環體內產生大量對象時。
2.規避構造方法的約束,因爲原型模式是在內存二進制流的拷貝,所以是不會調用構造方法的
原型模式的使用場景
1.類的初始化需要消耗非常多的資源(數據、硬件資源等)
2.性能和安全的要求,因爲通過new產生一個對象需要非常繁瑣的數據準備或訪問權限
克隆滿足的條件:
clone()方法將對象複製了一份並返還給調用者。所謂“複製”的含義與clone()方法是怎麼實現的。一般而言,clone()方法滿足以下的描述:
(1)對任何的對象x,都有:x.clone()!=x。換言之,克隆對象與原對象不是同一個對象。
(2)對任何的對象x,都有:x.clone().getClass() == x.getClass(),換言之,克隆對象與原對象的類型一樣。
(3)如果對象x的equals()方法定義其恰當的話,那麼x.clone().equals(x)應當成立的。
在JAVA語言的API中,凡是提供了clone()方法的類,都滿足上面的這些條件。JAVA語言的設計師在設計自己的clone()方法時,也應當遵守着三個條件。一般來說,上面的三個條件中的前兩個是必需的,而第三個是可選的。
可能分爲淺克隆和深克隆
先看一個例子,看看什麼是淺克隆:
public class Something implements Cloneable { private List<String> list = new ArrayList<String>(); // 重寫Object類中的clone方法 @Override protected Something clone() { Something something = null; try { something = (Something) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return something; } public void setValue(String value) { this.list.add(value); } public List<String> getList() { return this.list; } public static void main(String[] args) { //產生一個對象 Something something=new Something(); something.setValue("aaa"); //克隆一個對象 Something thing=something.clone(); thing.setValue("bbb"); System.out.println(something.getList());//結果:[aaa, bbb] } }
從輸出結果看,這並不是我們想要的拷貝,Object類提供的clone方法只是拷貝對象,對於對象內部的數組、引用對象都不拷貝,還是指向原生對象的內部元素,這種拷貝就是淺拷貝。
- 淺度克隆
只負責克隆按值傳遞的數據(比如基本數據類型、String類型),而不復制它所引用的對象,換言之,所有的對其他對象的引用都仍然指向原來的對象。
- 深度克隆
除了淺度克隆要克隆的值外,還負責克隆引用類型的數據。那些引用其他對象的變量將指向被複制過的新對象,而不再是原有的那些被引用的對象。換言之,深度克隆把要複製的對象所引用的對象都複製了一遍,而這種對被引用到的對象的複製叫做間接複製。
深度克隆要深入到多少層,是一個不易確定的問題。在決定以深度克隆的方式複製一個對象的時候,必須決定對間接複製的對象時採取淺度克隆還是繼續 採用深度克隆。因此,在採取深度克隆時,需要決定多深纔算深。此外,在深度克隆的過程中,很可能會出現循環引用的問題,必須小心處理。
下面以一個場景來敘述一下原型模式的使用,相信大家在大學考試的時候都有過打小抄的情況(沒有也不能說明什麼),上一面代理模式中說道小明同學找代理打遊戲,但是人就不去上課,整體的睡懶覺,好了一個學期過去,今天同小強說馬上要考試了,小明很驚訝的說:“不是吧,大學還有考試啊?”此處只能呵呵,那麼小明如何應對考試呢,當然是自己上了這麼多年學積累下來的經驗了,那就是抄別人的.....
代碼如下:
/** * 試卷 * */ public class Paper implements Cloneable { private Student student=new Student(); @Override protected Paper clone() { Paper paper = null; try { paper = (Paper) super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return paper; } // 獲取學生信息 public Student getStudent() { return this.student; } // 獲取考生姓名 public void setName(String name) { this.student.setName(name); } public int getResult() { return 29; } }
/** * * */ public class Student { // 學生姓名 private String name; // 學生學號 private String no; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNo() { return no; } public void setNo(String no) { this.no = no; } }
public class Client { public static void main(String[] args) { //生成一份試卷 Paper paper=new Paper(); //考生姓名 paper.setName("小強"); //小明覆制一份小強的試卷 Paper clonePaper=paper.clone(); clonePaper.setName("小明"); System.out.println(paper.getStudent().getName()); } }
這段程序的輸出結果是小明,那小強呢?這就是淺拷貝,結果就是兩份試卷的答題人都變成了小明。