flash做網(wǎng)站網(wǎng)站查詢?nèi)肟?/h1>
文章目錄
- 概述
- 類圖
- 原型模式優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn)
- 缺點(diǎn)
- 代碼實(shí)現(xiàn)

概述
在有些系統(tǒng)中,往往會(huì)存在大量相同或者是相似的對(duì)象,比如一個(gè)圍棋或者象棋程序中的旗子,這些旗子外形都差不多,只是演示或者是上面刻的內(nèi)容不一樣,若此時(shí)使用傳統(tǒng)的構(gòu)造函數(shù)創(chuàng)建對(duì)象,那么簡(jiǎn)單的對(duì)象還好,遇到稍微復(fù)雜一點(diǎn)的對(duì)象就會(huì)耗時(shí)耗資源,而使用原型設(shè)計(jì)模式會(huì)讓對(duì)象的生成高效很多
類圖
在本文中使用了人和身體的例子來(lái)演示原型模式,假如咱要造一個(gè)人的對(duì)象,需要設(shè)置人的屬性,姓名,年齡等,然后給他設(shè)置身體,大腦等器官,本例只是為了展示原型模式,只簡(jiǎn)單的做了Person和Body的結(jié)合。
原型模式主要有三個(gè)角色
1.抽象原型類:它定義了具體的原型對(duì)象必須實(shí)現(xiàn)的接口。如本例子中的IProtoType接口
2.具體原型類: 實(shí)現(xiàn)抽象原型接口中的復(fù)制對(duì)象的方法,比如本例中的實(shí)現(xiàn)了原型接口中的 clone(),deepClone()方法的Person類
3.訪問類: 使用具體原型類中的復(fù)制對(duì)象方法生成新的對(duì)象,比如本例中中的Client
結(jié)合本文中的例子,原型設(shè)計(jì)模式的類圖如下所示:
原型模式優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
原型設(shè)計(jì)模式的優(yōu)點(diǎn)主要有兩點(diǎn),如下所示:
(1)可以優(yōu)化性能:在JAVA語(yǔ)言中,可以通過實(shí)現(xiàn)Cloneable接口,重寫clone方法來(lái)達(dá)到復(fù)制對(duì)象的目的,這種方式是基于內(nèi)存二進(jìn)制流的復(fù)制,在性能上比直接使用new關(guān)鍵字創(chuàng)建一個(gè)對(duì)象高很多。
(2)可以使用原型模式中的深克隆方式保存對(duì)象的狀態(tài):我們可以使用原型模式將對(duì)象復(fù)制一份,并將其狀態(tài)保存起來(lái)。簡(jiǎn)化了創(chuàng)建對(duì)象的過程,在需要的時(shí)候直接使用我們保存的對(duì)象,例如遇到需要恢復(fù)到歷史某一狀態(tài)的需求時(shí),或者是需要實(shí)現(xiàn)撤銷操作的需求時(shí),都可以考慮原型模式
缺點(diǎn)
當(dāng)然,萬(wàn)事萬(wàn)物有優(yōu)點(diǎn)就會(huì)有缺點(diǎn),原型模式的缺點(diǎn)主要有三個(gè),如下所示:
(1)需要為每個(gè)類都配置一個(gè)克隆方法
(2)clone方法位于類的內(nèi)部,當(dāng)對(duì)已有的類進(jìn)行修改的時(shí)候,需要修改對(duì)應(yīng)的實(shí)現(xiàn)代碼。不符合開閉原則(對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放)
(3)當(dāng)實(shí)現(xiàn)深克隆時(shí),需要編寫較為復(fù)雜的代碼,而且當(dāng)對(duì)象之間存在多層嵌套引用時(shí),每一層對(duì)象對(duì)應(yīng)的類都必須支持深克隆,實(shí)現(xiàn)起來(lái)比較麻煩
深克隆:不僅拷貝對(duì)象的本身,而且拷貝對(duì)象包含的引用指向的所有對(duì)象
淺克隆:僅拷貝對(duì)象的本身,而不拷貝對(duì)象包含的引用指向的對(duì)象
使用一個(gè)例子解釋深克隆和淺克隆
public class Person {private static final Long VERSION = 1000L;private String name;private int age;private Body body;
// 省略構(gòu)造函數(shù)以及get,set方法
}
比如我們要拷貝上面的Person對(duì)象,如果使用深克隆的方式拷貝,這時(shí)候Person對(duì)象中包含的Body對(duì)象也會(huì)被拷貝,也就是說(shuō),深克隆拷貝出的對(duì)象和原來(lái)的對(duì)象是完全獨(dú)立的,我們修改新克隆出的對(duì)象,不會(huì)影響原來(lái)的Person對(duì)象。假如使用淺克隆,這時(shí)只會(huì)拷貝Person中包含的Body對(duì)象的引用,也就是說(shuō)使用淺克隆拷貝出來(lái)的新對(duì)象中包含的Body對(duì)象和原來(lái)的對(duì)象中包含的一樣,因?yàn)闇\克隆將Body的引用拷貝給了新克隆出的對(duì)象,這時(shí)候如果修改新克隆出的對(duì)象,那么原來(lái)的Person對(duì)象的Body也會(huì)跟著變,后面會(huì)有例子證實(shí)這點(diǎn)
代碼實(shí)現(xiàn)
本文中,我們使用人和身體的例子來(lái)演示原型設(shè)計(jì)模式。首先我們定義出原型模式的接口,如下所示:
public interface IPrototype extends Cloneable{Person deepClone() throws IOException, ClassNotFoundException;
}
原型模式的接口繼承自Java的Cloneable接口,其中包含了一個(gè)clone()方法,用于實(shí)現(xiàn)淺克隆,而我們定義的接口中
包含了一個(gè)deepClone()方法,用于實(shí)現(xiàn)深克隆。
然后就是定義一個(gè)類實(shí)現(xiàn)原型設(shè)計(jì)模式的接口:
public class Person implements IPrototype, Serializable {private static final Long VERSION = 1000L;private String name;private int age;private Body body;public Person(String name, int age) {this.name = name;this.age = age;}public void setBody(Body body) {this.body = body;}public Body getBody() {return body;}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;}@Overridepublic Person clone() {try {return (Person) super.clone();} catch (CloneNotSupportedException e) {throw new AssertionError();}}public Person deepClone() throws IOException, ClassNotFoundException {// 將對(duì)象寫入到流中ByteArrayOutputStream byteArrayOutputStream =new ByteArrayOutputStream();ObjectOutputStream outputStream =new ObjectOutputStream(byteArrayOutputStream);outputStream.writeObject(this);// 將對(duì)象從流中取出ByteArrayInputStream byteArrayInputStream =new ByteArrayInputStream(byteArrayOutputStream.toByteArray());ObjectInputStream inputStream =new ObjectInputStream(byteArrayInputStream);return (Person) inputStream.readObject();}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", body=" + body +'}';}
}
需要注意的是,為了實(shí)現(xiàn)深克隆,我們需要借助Java的Serializable 接口標(biāo)識(shí)本類可以被序列化。Person類中包含了基本類型的成員變量以及引用類型的成員變量Body,Body的定義如下:
public class Body implements Serializable {private static final Long VERSION = 1001L;private String sex;private String hand;public Body(String sex, String hand) {this.sex = sex;this.hand = hand;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getHand() {return hand;}public void setHand(String hand) {this.hand = hand;}@Overridepublic String toString() {return "Body{" +"sex='" + sex + '\'' +", hand='" + hand + '\'' +'}';}
}
為了能實(shí)現(xiàn)序列化,Body類也要實(shí)現(xiàn)Serializable接口。當(dāng)需要使用淺克隆的時(shí)候,我們就通過Person對(duì)象的clone()方法來(lái)生成,,當(dāng)需要使用深克隆的時(shí)候,我們就使用deepClone()方法來(lái)生成。
最后就是使用對(duì)應(yīng)的克隆方法生成克隆對(duì)象
public class Client {public static void main(String[] args) throws IOException, ClassNotFoundException {// 創(chuàng)建一個(gè)Person對(duì)象Person person = new Person("walt", 18);// 創(chuàng)建出Body對(duì)象Body body = new Body("男", "男生的手");person.setBody(body);System.out.println("原始的Person: " + person);// 使用淺克隆生成一個(gè)克隆對(duì)象Person clonePerson = person.clone();System.out.println("克隆的Person: " + clonePerson);// 獲取到克隆對(duì)象的Body并做修改Body cloneBody = clonePerson.getBody();cloneBody.setSex("女");cloneBody.setHand("女生的手");clonePerson.setBody(cloneBody);// 分別打印出克隆的對(duì)象和原始對(duì)象System.out.println("克隆Person: " + clonePerson + " ,原始Person: " + person);// 檢查克隆對(duì)象的body和原始對(duì)象的body是否是同一個(gè),是就為淺克隆,不是為深克隆System.out.println("克隆的Body是否等于原始的Body: " + (body == cloneBody));// 使用深克隆生成一個(gè)對(duì)象Person deepClonePerson = person.deepClone();// 獲取到深克隆后的person對(duì)象的body并修改Body deepCloneBody = deepClonePerson.getBody();deepCloneBody.setSex("深克隆男孩子");deepCloneBody.setHand("深克隆男生的手手");deepClonePerson.setBody(deepCloneBody);// 打印出深克隆后的對(duì)象和原始的對(duì)象System.out.println("深克隆Person: " + deepClonePerson + " ,原始Person: " + person);// 檢查克隆對(duì)象的body和原始對(duì)象的body是否是同一個(gè),是就為淺克隆,不是為深克隆System.out.println("深克隆的Body是否等于原始的Body: " + (body == deepCloneBody));}
}
運(yùn)行結(jié)果: