今天介紹原型模式,我本身偷偷給它命名爲克隆模式。由於原型模式的意圖是經過複製一個現有的對象來生成新的對象,而不是經過實例化的方式。java
1、原型模式概念數據庫
原型模式(Prototype Pattern):使用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。原型模式是一種對象建立型模式。編程
該接口用於建立當前對象的克隆。當直接建立對象的代價比較大時,則採用這種模式。例如,一個對象須要在一個高代價的數據庫操做以後被建立。咱們能夠緩存該對象,在下一個請求時返回它的克隆,在須要的時候更新數據庫,以此來減小數據庫調用。設計模式
須要注意的是經過克隆方法所建立的對象是全新的對象,它們在內存中擁有新的地址,一般對克隆所產生的對象進行修改對原型對象不會形成任何影響,每個克隆對象都是相互獨立的。經過不一樣的方式修改能夠獲得一系列類似但不徹底相同的對象。數組
2、原型模式結構圖緩存
在原型模式結構圖中包含以下幾個角色:網絡
原型模式的核心在於如何實現克隆方法,博主本人是從事Java開發,因此用Java語言提供的clone()方法來實現。學過Java語言的人都知道,全部的Java類都繼承自java.lang.Object。事實上,Object類提供一個clone()方法,能夠將一個Java對象複製一份。所以在Java中能夠直接使用Object提供的clone()方法來實現對象的克隆,Java語言中的原型模式實現很簡單。函數
須要注意的是可以實現克隆的Java類必須實現一個標識接口Cloneable,表示這個Java類支持被複制。若是一個類沒有實現這個接口可是調用了clone()方法,Java編譯器將拋出一個CloneNotSupportedException異常。this
克隆知足的條件spa
clone()方法將對象複製了一份並返還給調用者。所謂「複製」的含義與clone()方法是怎麼實現的有關。通常而言,clone()方法知足如下的描述:
在理解Java原型模式以前,首先須要理解Java中的一個概念:複製/克隆。Java中的對象複製/克隆分爲淺複製和深複製。在Java語言中,數據類型分爲值類型(基本數據類型)和引用類型,值類型包括int、double、byte、boolean、char等簡單數據類型,引用類型包括類、接口、數組等複雜類型。淺克隆和深克隆的主要區別在因而否支持引用類型的成員變量的複製,下面將對二者進行詳細介紹。
3、淺克隆
在淺克隆中,若是原型對象的成員變量是值類型,將複製一份給克隆對象;若是原型對象的成員變量是引用類型,則將引用對象的地址複製
一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同
的內存地址。簡單來講,在淺克隆中,當對象被複制時只複製它自己和其中包含的值類型的成員變量,而引用類型的成員對象並無複製。下面以複製一本書爲例。
下面是Author源代碼:
public class Author { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
下面是Book源代碼:
public class Book implements Cloneable{ private String bookName; private int price; private Author author; public Book clone() { Book book=null; try { book=(Book)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return book; } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } }
下面是客戶端類:
public class Client { public static void main(String[] args){ Author author=new Author(); author.setName("tengj"); Book book=new Book(); book.setBookName("Java設計模式"); book.setPrice(99); book.setAuthor(author); Book book2=book.clone(); System.out.println(book==book2); // false System.out.println(book.getBookName() == book2.getBookName()); // true System.out.println(book.getAuthor() == book2.getAuthor()); // true } }
由輸出的結果能夠驗證說到的結論。由此咱們發現:雖然複製出來的對象從新在堆上開闢了內存空間,可是,對象中各屬性確保持相等。對於基本數據類型很好理解,但對於引用數據類型來講,則意味着此引用類型的屬性所指向的對象自己是相同的, 並無從新開闢內存空間存儲。換句話說,引用類型的屬性所指向的對象並無複製。由此,咱們將其稱之爲淺複製。當複製後的對象的引用類型的屬性所指向的對象也從新得以複製,此時,稱之爲深複製。
4、深克隆
在深克隆中,不管原型對象的成員變量是值類型仍是引用類型,都將複製一份給克隆對象,深克隆將原型對象的全部引用對象也複製一份給克隆對象。簡單來講,在深克隆中,除了對象自己被複制外,對象所包含的全部成員變量也將複製。
在Java語言中,若是須要實現深克隆,能夠經過序列化(Serialization)等方式來實現。序列化就是將對象寫到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在於內存中。經過序列化實現的拷貝不只能夠複製對象自己,並且能夠複製其引用的成員對象,所以經過序列化將對象寫到一個流中,再從流裏將其讀出來,能夠實現深克隆。須要注意的是可以實現序列化的對象其類必須實現Serializable接口,不然沒法實現序列化操做。
仍是以複製一本書爲例。下面是Author源代碼(Author也須要實現Serializable接口!!):
public class Author implements Serializable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
下面是Book源代碼(Book類須要實現Serializable接口):
public class Book implements Serializable{ private String bookName; private int price; private Author author; public Book deepClone() throws IOException, ClassNotFoundException { // 寫入當前對象的二進制流 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); // 讀出二進制流產生的新對象 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (Book) ois.readObject(); } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } }
下面是客戶端類:
public class DeepClient { public static void main(String[] args) throws IOException, ClassNotFoundException { Author author=new Author(); author.setName("tengj"); Book book=new Book(); book.setBookName("Java設計模式"); book.setPrice(99); book.setAuthor(author); Book book2=book.deepClone(); System.out.println(book==book2); // false System.out.println(book.getBookName() == book2.getBookName()); // false System.out.println(book.getAuthor() == book2.getAuthor()); // false } }
從輸出結果中能夠看出,深複製不只在堆內存上開闢了空間以存儲複製出的對象,甚至連對象中的引用類型的屬性所指向的對象也得以複製,從新開闢了堆空間存儲。
5、總結
原型模式做爲一種快速建立大量相同或類似對象的方式,在軟件開發中應用較爲普遍,不少軟件提供的複製(Ctrl + C)和粘貼(Ctrl + V)操做就是原型模式的典型應用,下面對該模式的使用效果和適用狀況進行簡單的總結。
一、主要優勢
原型模式的主要優勢以下:
二、主要缺點
原型模式的主要缺點以下:
三、適用場景
在如下狀況下能夠考慮使用原型模式: