不知不覺(jué)這個(gè)系列已寫(xiě)了3篇了,其實(shí)很早之前就想寫(xiě)設(shè)計(jì)模式了,只不過(guò)怕自己誤人子弟沒(méi)有提筆去寫(xiě)。后來(lái)在實(shí)際開(kāi)發(fā)中,發(fā)現(xiàn)設(shè)計(jì)模式可讓1個(gè)開(kāi)發(fā)人員融會(huì)貫通所學(xué)的知識(shí),為了進(jìn)1步鞏固自己,就寫(xiě)下了這1些列文章。前面介紹了3個(gè)模式。
本篇文章介紹的模式其實(shí)很簡(jiǎn)單,即原型模式,依照慣例,先看定義。
用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并通過(guò)拷貝這些原型創(chuàng)建新的對(duì)象。
這是甚么鬼哦,本寶寶看不懂!沒(méi)必要過(guò)度在乎這些定義,自己心里明白就ok了。沒(méi)代碼你說(shuō)個(gè)jb。
首先我們定義1個(gè)Person類
public class Person{ private String name; private int age; private double height; private double weight; public Person(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } @Override public String toString() { return "Person{" + "name=" + name + + ", age=" + age + ", height=" + height + ", weight=" + weight + }; } }
要實(shí)現(xiàn)原型模式,只需要依照下面的幾個(gè)步驟去實(shí)現(xiàn)便可。
測(cè)試1下
public class Main { public static void main(String [] args){ Person p=new Person(); p.setAge(18); p.setName("張3"); p.setHeight(178); p.setWeight(65); System.out.println(p); Person p1= (Person) p.clone(); System.out.println(p1); p1.setName("李4"); System.out.println(p); System.out.println(p1); } }
輸出結(jié)果以下
Person{name=’張3’, age=18, height=178.0, weight=65.0}
Person{name=’張3’, age=18, height=178.0, weight=65.0}
Person{name=’張3’, age=18, height=178.0, weight=65.0}
Person{name=’李4’, age=18, height=178.0, weight=65.0}
試想1下,兩個(gè)不同的人,除姓名不1樣,其他3個(gè)屬性都1樣,用原型模式進(jìn)行拷貝就會(huì)顯得異常簡(jiǎn)單,這也是原型模式的利用場(chǎng)景之1。
1個(gè)對(duì)象需要提供給其他對(duì)象訪問(wèn),而且各個(gè)調(diào)用者可能都需要修改其值時(shí),可以斟酌使用原型模式拷貝多個(gè)對(duì)象供調(diào)用者使用,即保護(hù)性拷貝。
但是假定Person類里還有1個(gè)屬性叫興趣愛(ài)好,是1個(gè)List集合,就像這模樣
private ArrayListhobbies=new ArrayList(); public ArrayListgetHobbies() { return hobbies; } public void setHobbies(ArrayListhobbies) { this.hobbies = hobbies; }
在進(jìn)行拷貝的時(shí)候要格外注意,如果你直接按之前的代碼那樣拷貝
@Override public Object clone(){ Person person=null; try { person=(Person)super.clone(); person.name=this.name; person.weight=this.weight; person.height=this.height; person.age=this.age; person.hobbies=this.hobbies; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return person; }
會(huì)帶來(lái)1個(gè)問(wèn)題
使用測(cè)試代碼進(jìn)行測(cè)試
public class Main { public static void main(String [] args){ Person p=new Person(); p.setAge(18); p.setName("張3"); p.setHeight(178); p.setWeight(65); ArrayListhobbies=new ArrayList(); hobbies.add("籃球"); hobbies.add("編程"); hobbies.add("長(zhǎng)跑"); p.setHobbies(hobbies); System.out.println(p); Person p1= (Person) p.clone(); System.out.println(p1); p1.setName("李4"); p1.getHobbies().add("游泳"); System.out.println(p); System.out.println(p1); } }
我們拷貝了1個(gè)對(duì)象,并添加了1個(gè)興趣愛(ài)好進(jìn)去,看下打印結(jié)果
Person{name=’張3’, age=18, height=178.0, weight=65.0, hobbies=[籃球, 編程, 長(zhǎng)跑]}
Person{name=’張3’, age=18, height=178.0, weight=65.0, hobbies=[籃球, 編程, 長(zhǎng)跑]}
Person{name=’張3’, age=18, height=178.0, weight=65.0, hobbies=[籃球, 編程, 長(zhǎng)跑, 游泳]}
Person{name=’李4’, age=18, height=178.0, weight=65.0, hobbies=[籃球, 編程, 長(zhǎng)跑, 游泳]}
你會(huì)發(fā)現(xiàn)原來(lái)的對(duì)象的hobby也產(chǎn)生了變換。
其實(shí)致使這個(gè)問(wèn)題的本質(zhì)緣由是我們只進(jìn)行了淺拷貝,也就是只拷貝了援用,終究?jī)蓚€(gè)對(duì)象指向的援用是同1個(gè),1個(gè)產(chǎn)生變化另外一個(gè)也會(huì)產(chǎn)生變換,明顯解決方法就是使用深拷貝。
@Override public Object clone(){ Person person=null; try { person=(Person)super.clone(); person.name=this.name; person.weight=this.weight; person.height=this.height; person.age=this.age; person.hobbies=(ArrayList)this.hobbies.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return person; }
注意person.hobbies=(ArrayList)this.hobbies.clone();,不再是直接援用而是進(jìn)行了1份拷貝。再運(yùn)行1下,就會(huì)發(fā)現(xiàn)原來(lái)的對(duì)象不會(huì)再產(chǎn)生變化了。
Person{name=’張3’, age=18, height=178.0, weight=65.0, hobbies=[籃球, 編程, 長(zhǎng)跑]}
Person{name=’張3’, age=18, height=178.0, weight=65.0, hobbies=[籃球, 編程, 長(zhǎng)跑]}
Person{name=’張3’, age=18, height=178.0, weight=65.0, hobbies=[籃球, 編程, 長(zhǎng)跑]}
Person{name=’李4’, age=18, height=178.0, weight=65.0, hobbies=[籃球, 編程, 長(zhǎng)跑, 游泳]}
其實(shí)有時(shí)候我們會(huì)更多的看到原型模式的另外一種寫(xiě)法。
其實(shí)都差不多,只是寫(xiě)法不1樣。
現(xiàn)在來(lái)挖挖android中的原型模式。
先看Bundle類,該類實(shí)現(xiàn)了Cloneable接口
public Object clone() { return new Bundle(this); } public Bundle(Bundle b) { super(b); mHasFds = b.mHasFds; mFdsKnown = b.mFdsKnown; }
然后是Intent類,該類也實(shí)現(xiàn)了Cloneable接口
@Override public 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; this.mContentUserHint = o.mContentUserHint; if (o.mCategories != null) { this.mCategories = new ArraySet(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); } }
用法也顯得10分簡(jiǎn)單,1旦我們要用的Intent與現(xiàn)有的1個(gè)Intent很多東西都是1樣的,那我們就能夠直接拷貝現(xiàn)有的Intent,再修改不同的地方,即可以直接使用。
Uri uri = Uri.parse("smsto:10086"); Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri); shareIntent.putExtra("sms_body", "hello"); Intent intent = (Intent)shareIntent.clone() ; startActivity(intent);
網(wǎng)絡(luò)要求中1個(gè)最多見(jiàn)的開(kāi)源庫(kù)OkHttp中,也利用了原型模式。它就在OkHttpClient這個(gè)類中,它實(shí)現(xiàn)了Cloneable接口
/** Returns a shallow copy of this OkHttpClient. */ @Override public OkHttpClient clone() { return new OkHttpClient(this); } private OkHttpClient(OkHttpClient okHttpClient) { this.routeDatabase = okHttpClient.routeDatabase; this.dispatcher = okHttpClient.dispatcher; this.proxy = okHttpClient.proxy; this.protocols = okHttpClient.protocols; this.connectionSpecs = okHttpClient.connectionSpecs; this.interceptors.addAll(okHttpClient.interceptors); this.networkInterceptors.addAll(okHttpClient.networkInterceptors); this.proxySelector = okHttpClient.proxySelector; this.cookieHandler = okHttpClient.cookieHandler; this.cache = okHttpClient.cache; this.internalCache = cache != null ? cache.internalCache : okHttpClient.internalCache; this.socketFactory = okHttpClient.socketFactory; this.sslSocketFactory = okHttpClient.sslSocketFactory; this.hostnameVerifier = okHttpClient.hostnameVerifier; this.certificatePinner = okHttpClient.certificatePinner; this.authenticator = okHttpClient.authenticator; this.connectionPool = okHttpClient.connectionPool; this.network = okHttpClient.network; this.followSslRedirects = okHttpClient.followSslRedirects; this.followRedirects = okHttpClient.followRedirects; this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure; this.connectTimeout = okHttpClient.connectTimeout; this.readTimeout = okHttpClient.readTimeout; this.writeTimeout = okHttpClient.writeTimeout; }
正如開(kāi)頭的注釋Returns a shallow copy of this OkHttpClient,該clone方法返回了1個(gè)當(dāng)前對(duì)象的深拷貝對(duì)象。