願你生命中有夠多的雲翳,造就一個美好的黃昏
介紹
原型模式(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嗎?