願你生命中有夠多的雲翳,造就一個美好的黃昏
介紹
原型模式(Prototype Pattern)是用於創建重復的對象,同時又能保證性能,這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。我們在程序設計的時候通常會創建很多對象,並且隨着不斷地開發,會發現在使用過程很多對象是相似甚至是相同的,如果沒有使用原型模式,那么我們需要進行下面類似的操作(翻出以前的代碼),大量的set方法,這代碼看起來十分的“工整”,“干凈”,但是全部是無腦式的體力勞動,沒有半點技術含量,這種方式不僅浪費了大量的時間去寫,其效率還不高,因為需要進行對象的初始化,還要一個一個的賦值,原型模式就是為了來解決這種無腦式的操作。
想必克隆這個詞我們大家都很熟悉,1996年在英國愛丁堡市羅斯林研究所克隆出了世界上第一個克隆生物-克隆羊多利,我們的原型模式也是運用了克隆這個思想,下面我們都將其稱為拷貝,原型模式就是通過拷貝原型對象,然后再創建出一個同拷貝對象一樣的對象(但是它們並不是同一個對象,只是類型相同罷了),只不過他不是通過對象的構造函數進行賦值而來的,而是基於內存的二進制流進行復制,所以效率比new一個對象進行復制要高效。
Java為我們內置了原型接口(Cloneable),如果要進行對象的拷貝,必須實現Cloneable接口,它是一個空接口,里面沒有任何的方法,它只是一個規約而已,我們實現Cloneable接口只需要在類里面重寫Object的clone()方法,這樣我們就可以進行對象的拷貝,注意:如果沒有實現Cloneable接口,直接使用Object的clone()方法,那么將會拋出異常(CloneNotSupportedException),因為JVM里面識別該類沒有實現Cloneable接口,不能安全的進行拷貝,我們看一下它們的實現關系
下面我們進行實際的操作:
假如我有一個女朋友,她的身高170cm,體重55kg,胸圍90,人長得漂亮,就是不太溫柔,因為和她在一起的時間的長了,所以有些膩了,不過我還是喜歡她的身高和美貌,我想復制出一個身高,體重,胸圍和她一樣的女孩子,只不過我希望復制出的女孩子非常溫柔,善解人意,因為我手里有原型工具,所以我直接開始了,我按照自己的要求輸入,然后等待復制,不一會兒,我想要的女孩子出來了,它們一模一樣,但是復制出來的女孩子非常溫柔。我們具體代碼實現一下。
一.首先定義一個原型類;讓其實現Cloneable接口,兵重寫Object clone()方法
我們充clone()方法中看出它使用了super.clone()進行拷貝,super.clone()是直接使用內存的二進制流進行復制,在復制的時候會為目標對象分配一塊內存,所以它們的地址是不相同的,只是類型是相同的。
由上看出clone()方法由native修飾,可知它調用的不是java層面的方法,而是JVM里面的方法(JVM部分是使用C++進行編寫),自然不是基於java層面的構造函數進行復制。
二.客戶端調用
由圖可知,我已經復制出一個我喜歡的類型的新女朋友了,她和我原女友在生理上有相同的基因,只不過她很溫柔體貼,是我喜愛的類型,到了某一天,我根據她們兩個的性格給她們兩個安排不同的任務,首先,她們兩個都要會做飯,還要會洗衣服,由於原女友脾氣火爆,我不讓她給我按摩洗腳,我讓溫柔的新女友給我按摩洗腳,我又一次對她們兩個進行改造,我讓她們兩個開始做任務了。
但是這下出意外了,大事不好了,我只是讓新女友給我按摩洗腳,現在原女友也給我按摩洗腳,他脾氣那么火爆,怕是要把我搞死,我仔細想了一下問題出在哪里,苦思冥想許久,原來她們的身高,體重,胸圍這些是基本的要求(基本類型),但是她們的任務就不是基本類型了(引用類型),所以在改造過程中出現了問題,導致不該分配的任務分配給了原女友,於是查了很多資料后原來我是對她們進行了淺拷貝,所以才會這樣,為了我的生命安危,我需要再次進行改造。
對她們進行深拷貝
一,對原型類進行一個改造,加上了序列化接口Serializable,它也是一個空接口,我們寫了deepClone()方法,使用流和序列化的方式進行復制。
二,客戶端調用。
發現,原女友沒有按摩洗腳的任務了,小命終於保住了,使用了深克隆后,我們發現她們兩個的任務不再一樣了,為什么之前使用淺克隆是一樣的呢,因為她們的任務是一個引用類型,使用淺克隆時復制的其實不是它們的值,而是它們的地址,使用了深克隆后,它們是以流的形式進行復制。
以上是我通過復制女朋友的例子來理解原型模式,只不過現實中我依然還是個單身狗,其實從上面的例子中我們發現只有身高,體重,胸圍這三個屬性的值沒有發生變化,其他的屬性我們都重新進行了賦值,那么你可能會說,這沒必要使用原型模式吧,是的,少得話可能體現不出來,但是如果這個類的屬性特別多,又特別復雜的時候,你還會選擇一個一個的取set嗎?