標籤:android style blog http color io 使用 ar java
摘要:
1.本文將詳細介紹原型模式的原理和實際代碼中特別是Android系統代碼中的應用。
綱要:
1. 引入原型模式
2. 原型模式的概念及優缺點介紹
3. 原型模式對拷貝的使用
4. 原型模式在Android源碼中的應用
1.先來一個段子:
GG和MM經常在QQ上聊天,但是GG打字的速度慢如蝸牛爬行,每次MM在瞬間完成恢複或者問候是,GG都會很緊張的去儘力快速打字,儘管如此,還是讓MM有些不高興,MM說回複資訊這麼慢,顯然是用心不專,不在乎她。哎,GG也是百口難辯啊,不過也確實是沒有辦法。
有一天,GG想自己的密友K傾訴了自己的苦衷。K頓生大笑。說道:“傻瓜,你怎麼不去網上收集一些肉麻的情話以及一些你們經常說話會涉及到主題,把這些東西拷貝下來儲存在自己的電腦或者隨身碟裡面,這樣一來如果下次在聊天就可以借用過來了!”,“K就是K,我怎麼沒有想到呢~妙極~妙極^_^”,“不過不要太高興,這些東西是要適當修改的,要不然你把名字都搞錯的話,就等著你的MM把你踹死吧O(∩_∩)O哈哈~”K補充道,“嗯,說的對,謝謝K哥解決了我的心腹之患啊”GG樂不可支的說道。
這是MM由在網上和GG聊天,GG專門複製那些實現準備好的肉麻情話經過稍加修改後發給MM,MM都快美死了…(摘自Android之大話設計模式)
2.原型模式介紹
2.1什麼是原型模式?
在類A建立執行個體的時候,會將傳入的類B的執行個體(這個執行個體被稱為原型對象)進行複製,以此快速的獲得與傳入對象一致的初始化資料。複製是原型模式最顯著特點。
類A被稱為隔離類,因為原型模式將類A隔離起來,不需要關注類B的變化。
類B稱為原型類,實現了clone()介面給類A複製。類B是易變的,但是實現的介面不易變。類B通常是一個抽象類別,傳入給類A的執行個體是類B的子類的執行個體。
2.2這樣做有什麼好處?
1、 方便。如果一個原型對象本身資料非常多,一個一個賦值是不現實的。通過進行複製,我們可以快速的獲得這個原型對象的全部資料。
2、 耦合度低。如果原型對象是個“易變類”,即這個類可能會有各種各樣的繼承,或者這個類隨著開發的進行可能會不斷的修改。但只要類B實現的介面是穩定不易變的, 原型模式可以使隔離類保持介面的穩定性,不需要隨著類的變化而變化。
3.1關於拷貝:
複製有兩種,淺拷貝和深拷貝。淺拷貝就是只複製一次,對被複製的類的所有屬性進行直接的賦值,而不是遞迴的複製。遞迴的複製就是對自己的屬性也調用它自己的複製方法。對於基本類型(七種基本類型boolean,char,byte,short,float,double.long)來說,賦值將得到此基本類型的一份拷貝。如果是非基本類型,則得到引用的拷貝。引用的拷貝意味著什嗎?意味著直接賦值之後,這兩個普通類的執行個體的指向的資料空間是同一個。也就是說它們的修改會互相影響。我們舉一個例子:
兩個類A,B,它們的屬性如下:
Class A { int a1; String a2; B b;}Class B { int b1; String b2;}
如果我們要實現Class A的淺拷貝,它的clone方法將會是這樣的:
Object clone() { return super.clone();}
萬類之王Object自己已經實現了clone方法,我們只需要調用就可以了,在這裡Object.clone()其實等價於:
A aa = new A();aa.a1 = this.a1;aa.a2 = this.a2;aa.b = this.b;return a;
當然,這隻是等價,不是真正的實現方法,畢竟Object不知道這個Class A有什麼屬性。萬類之王是通過位元組拷貝實現的,調用的是native方法。Class B是非基本類型,所以直接賦值只會得到b引用的拷貝,新複製出來的aa執行個體和原執行個體a指向了同一個b執行個體的資料空間。所以對a.b的改動會影響到原執行個體a的b改動。
如果是深拷貝,我們應該這樣實現:
Object clone() { A aa = (A)super.clone(); aa.b = (B)a.b.clone(); return (Object)aa;}
其實就是把非基本類型的屬性再進行一次複製就可以了,如果該屬性中仍有非基本類型,則需要同樣實現深拷貝。這裡也就暴露出原型模式的缺點:實現原型模式的類的屬性需要全部實現clone(),否則無法實現該模式。對於一個新建立的類來說也許不難,但如果中後期加入,則會需要大量的改動。
因為Java故意的隱瞞了指標這個概念,導致這裡的理解有點複雜。從C語言的角度來看,int,float等類型為基本類型,他們本身在聲明的時候就擁有了自己儲存值的空間,賦值的時候是把值重新拷貝了一份。而自己定義的struct結構體,我們聲明的是對應的struct*指標,不同指標指向同一個記憶體塊,它們的修改會互相影響。
3.2Interger,String等封裝類的拷貝問題:
細心的同學可能發現了,String不是基礎類型,為什麼在上面直接賦值就可以了?String的儲存實際上通過char[]來實現的,同樣,Interger是int的封裝類。封裝類的特質之一就是在對其值進行操作時會體現出其對應的基本類型的性質。關於該問題的詳細情況可以訪問:http://freej.blog.51cto.com/235241/168676。
3.3原型模式使用的是哪種拷貝呢?
設計模式裡並沒有指定原型模式是採用哪種拷貝,我們應根據實際情況選擇深拷貝和淺拷貝。當然深拷貝應用的情境要多很多。此外,拷貝也沒有指定需要拷貝所有屬性,也是根據實際情況選擇就可以了。
4.Android源碼實戰舉例:
例子:Intent
Intent是一個實現了簡單的原型模式的類。它的clone是這樣的:
@Overridepublic Object clone() { return new Intent(this);}public Intent(Intent o) { this.mAction = o.mAction; this.mData = o.mData; this.mType = o.mType; this.mPackage = o.mPackage; this.mComponent = o.mComponent; this.mFlags = o.mFlags; //下面幾個是引用對象被重新建立了,是深拷貝 if (o.mCategories != null) { this.mCategories = new HashSet<String>(o.mCategories); } if (o.mExtras != null) { this.mExtras = new Bundle(o.mExtras); } if (o.mSourceBounds != null) { this.mSourceBounds = new Rect(o.mSourceBounds); } if (o.mSelector != null) { this. mSelector = new Intent(o. mSelector); } if (o.mClipData != null) { this. mClipData = new ClipData(o. mClipData); }}
所以,Intent是一個深拷貝。有趣的是,它的屬性也是使用構造器這種方式進行拷貝。在這種情況下,上文提到的“隔離類”和“易變類”都是自己。
new Intent(Intent o)這種構造方法在源碼中屢見不鮮。如Launcher3中的LauncherModel.java,frameworks中的LauncherActivity.java,Settings中的DockEventReceiver。我們可以通過搜尋“Intent(intent”或“Intent(mIntent”看到。
著作權,轉載請註明出處:
http://www.cnblogs.com/sickworm/p/4016081.html
【設計模式】原型模式(Prototype)