摘要:定義給出的原型模式定義如下使用原型實例指定將要創建的對象類型,通過復制這個實例創建新的對象。具體原型類角色負責實現復制現有實例并生成新實例的方法。
Java面試通關手冊(Java學習指南,歡迎Star,會一直完善下去,歡迎建議和指導):https://github.com/Snailclimb/Java_Guide
系列文章回顧:設計模式專欄
深入理解單例模式
深入理解工廠模式
深入理解建造者模式 ——組裝復雜的實例
歷史文章推薦:一只準程序猿的嘮叨
可能是最漂亮的Spring事務管理詳解
Java多線程學習(八)線程池與Executor 框架
面試中關于Redis的問題看這篇就夠了
目錄:[TOC]
一 原型模式介紹在面向對象系統中,使用原型模式來復制一個對象自身,從而克隆出多個與原型對象一模一樣的對象。
另外在軟件系統中,有些對象的創建過程較為復雜,而且有時候需要頻繁創建,原型模式通過給出一個原型對象來指明所要創建的對象的類型,然后用復制這個原型對象的辦法創建出更多同類型的對象,這就是原型模式的意圖所在。
1.1 定義GOF給出的原型模式定義如下:
Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype. (使用原型實例指定將要創建的對象類型,通過復制這個實例創建新的對象。)1.2 原型模式適用場景
我們現在一般會使用new關鍵字指定類名生成類的實例(PS:我們以前使用java.lang.Cloneable的一個很大原因是使用new創建對象的速度相對來說會慢一些,隨著JVM性能的提升,new的速度和Object的clone()方法的速度差不多了。)。
使用new關鍵字創建類的時候必須指定類名,但是在開發過程中也會有“在不指定類名的前提下生成實例”的需求。例如,在下面這些情況下,就需要根據現有的實例來生成新的實例。
1) 對象種類繁多,無法將他們整合到一個類的時候;
2) 難以根據類生成實例時;
3) 想解耦框架與生成的實例時。
如果想要讓生成實例的框架不再依賴于具體的類,這時,不能指定類名來生成實例,而要事先“注冊”一個“原型”實例,然后通過復制該實例來生成新的實例。
1.3 模式分析在原型模式結構中定義了一個抽象原型類,所有的Java類都繼承自 java.lang.Object,而Object類提供一個clone()方法,可以將一個Java對象復制一份。因此在Java中可以直接使用Object提供的clone()方法來實現對象的克隆,Java語言中的原型模式實現很簡單。
能夠實現克隆的Java類必須實現一個標識接口Cloneable,表示這個Java類支持復制。如果一個類沒有實現這個接口但是調用了clone()方法,Java編譯器將拋出一個CloneNotSupportedException異常。
注意: `java.lang.Cloneable 只是起到告訴程序可以調用clone方法的作用,它本身并沒有定義任何方法。
在使用原型模式克隆對象時,根據其成員對象是否也克隆,原型模式可以分為兩種形式:深克隆 和 淺克隆 。
關于深克隆 和 淺克隆 的詳細內容可以參考:詳解Java中的clone方法
1.4 模式優缺點分析原型模式的優點:
當創建新的對象實例較為復雜時,使用原型模式可以簡化對象的創建過程,通過一個已有實例可以提高新實例的創建效率。
可以動態增加或減少產品類。
原型模式提供了簡化的創建結構。
可以使用深克隆的方式保存對象的狀態。
原型模式的缺點:
需要為每一個類配備一個克隆方法,而且這個克隆方法需要對類的功能進行通盤考慮,這對全新的類來說不是很難,但對已有的類進行改造時,不一定是件容易的事,必須修改其源代碼,違背了“開閉原則”。
在實現深克隆時需要編寫較為復雜的代碼。
二 示例程序下面示例程序的作用是將字符串放入方框中顯示出來或者是加了下劃線顯示出來。
類和接口一覽表:
示例程序類圖:
Product接口是復制功能接口,該接口繼承了java.lang.Cloneable(只有實現了該接口的類的實例才可以調用clone()方法復制實例,否則會拋出異常).
另外需要注意:`java.lang.Cloneable 只是起到告訴程序可以調用clone方法的作用,它本身并沒有定義任何方法。
package prototype_pattern; public interface Product extends Cloneable{ //use方法是用于“使用”的方法,具體怎么“使用”,則被交給子類去實現。 public abstract void use(String s); //creatClone方法是用于復制實例的方法 public abstract Product creatClone(); }2.2 Manager類(Client)
Manager類使用Product接口來復制實例。
Product接口以及Manager類的代碼完全沒有出現在MessageBox類和UnderlinePen類的名字,因此這意味著我們可以獨立地修改Product接口以及Manager類,不受MessageBox類和UnderlinePen類的影響。這是非常重要的,因為 一旦在類中使用到了別的類名,就意味著該類與其他類緊密的地耦合在了一起 。在Manager類中,并沒有寫明具體的類名, 僅僅使用了Product這個接口名。也就是說,Product接口成為了連接Manager類與其他具體類之間的橋梁。
package prototype_pattern; import java.util.HashMap; public class Manager { //保存實例的“名字”和“實例”之間的對應關系 private HashMap2.3 MessageBox類(ConcreteProtorype)showcase=new HashMap (); //register方法將接收到的一組“名字”和“Product接口”注冊到showcase中。這里Product是實現Product接口的實例,具體還未確定 public void register(String name ,Product product){ showcase.put(name, product); } public Product create(String productname){ Product p=showcase.get(productname); return p.creatClone(); } }
裝飾方框樣式的具體原型,實現了 Product接口,實現復制現有實例并生成新實例的方法。
package prototype_pattern; public class MessageBox implements Product { //保存的是裝飾方框使用的字符樣式 private char decochar; public MessageBox(char decochar) { this.decochar = decochar; } @Override public void use(String s) { int length=s.getBytes().length; for (int i = 0; i < length+4; i++) { System.out.print(decochar); } System.out.println(""); System.out.println(decochar+" "+s+" "+decochar); for (int i = 0; i < length+4; i++) { System.out.print(decochar); } System.out.println(""); } //該方法用于復制自己 @Override public Product creatClone() { Product p=null; try { p=(Product) clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return p; } }
只有類自己(或是它的子類)能夠調用Java語言中定義的clone方法。當其他類要求復制實例時,必須先調用createClone這樣的方法,然后在該方法內部在調用clone方法。
2.4 UnderlinePen類(ConcreteProtorype)下劃線樣式的具體原型,實現了Product接口,用于實現復制現有實例并生成新實例的方法。UnderlinePen類的實現幾乎和 MessageBox類一樣,不同的可能只是use方法的實現。
package prototype_pattern; public class UnderlinePen implements Product { private char ulchar; public UnderlinePen(char ulchar) { this.ulchar = ulchar; } @Override public void use(String s) { int length = s.getBytes().length; System.out.println("""+s+"""); for (int i = 0; i2.5 Main類 Main類首先生成Manager實例。接著,在Manager實例中通過`register方法注冊了UnderlinePen類的實例(帶名字)和MessageBox類的實例(帶名字)。
package prototype_pattern; public class Main { public static void main(String[] args) { Manager manager = new Manager(); UnderlinePen underlinePen=new UnderlinePen("~"); MessageBox mbox=new MessageBox("*"); MessageBox sbox=new MessageBox("/"); manager.register("Strong message", underlinePen); manager.register("Waring Box", mbox); manager.register("Slash Box", sbox); Product p1=manager.create("Strong message"); p1.use("hello world"); Product p2=manager.create("Waring Box"); p2.use("hello world"); Product p3=manager.create("Slash Box"); p3.use("hello world"); } }運行結果:
三 原型模式的角色分析通過上面的例子,相信大家對于原型模式有了更進一步的認識,下面我們看看原型模式的幾個登場角色。
3.1 Prototype(抽象原型類)Product角色負責定義用于復制現有實例來生成新實例的方法。在示例程序中的Product接口就是該角色。
3.2 ConcretePrototype(具體原型類)ConcretePrototype角色負責實現復制現有實例并生成新實例的方法。在示例程序中,MessageBox和UnderlinePen都是該角色。
3.3 Client(客戶類/使用者)Client角色負責使用復制實例的方法生成新的實例。在示例程序中,Manager類扮演的就是該角色。
Prototype模式的類圖:
四 原型模式的實際應用案例(1) 原型模式應用于很多軟件中,如果每次創建一個對象要花大量時間,原型模式是最好的解決方案。很多軟件提供的復制(Ctrl + C)和粘貼(Ctrl + V)操作就是原型模式的應用,復制得到的對象與原型對象是兩個類型相同但內存地址不同的對象,通過原型模式可以大大提高對象的創建效率。
(2) 在Struts2中為了保證線程的安全性,Action對象的創建使用了原型模式,訪問一個已經存在的`Action對象時將通過克隆的方式創建出一個新的對象,從而保證其中定義的變量無須進行加鎖實現同步,每一個Action中都有自己的成員變量,避免Struts1因使用單例模式而導致的并發和同步問題。
(3) 在Spring中,用戶也可以采用原型模式來創建新的bean實例,從而實現每次獲取的是通過克隆生成的新實例,對其進行修改時對原有實例對象不造成任何影響。
五 總結本文主要介紹了:什么是原型模式、原型模式的優缺點以及使用場景。另外,簡單介紹了深拷貝和淺拷貝以及原型模式的實際應用案例。
參考:
《圖解設計模式》
歡迎關注我的微信公眾號:"Java面試通關手冊"(一個有溫度的微信公眾號,無廣告,單純技術分享,期待與你共同進步~~~堅持原創,分享美文,分享各種Java學習資源。)最后,就是使用阿里云服務器一段時間后,感覺阿里云真的很不錯,就申請做了阿里云大使,然后這是我的優惠券地址.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71210.html
摘要:首先,需要來理清一些基礎的計算機編程概念編程哲學與設計模式計算機編程理念源自于對現實抽象的哲學思考,面向對象編程是其一種思維方式,與它并駕齊驅的是另外兩種思路過程式和函數式編程。 JavaScript 中的原型機制一直以來都被眾多開發者(包括本人)低估甚至忽視了,這是因為絕大多數人沒有想要深刻理解這個機制的內涵,以及越來越多的開發者缺乏計算機編程相關的基礎知識。對于這樣的開發者來說 J...
摘要:我們用一張圖表示構造函數和實例原型之間的關系好了構造函數和實例原型之間的關系我們已經梳理清楚了,那我們怎么表示實例與實例原型,也就是或者和之間的關系呢。 開篇: 在Brendan Eich大神為JavaScript設計面向對象系統的時候,借鑒了Self 和Smalltalk這兩門基于原型的語言,之所以選擇基于原型的面向對象系統,并不是因為時間匆忙,它設計起來相對簡單,而是因為從一開始B...
摘要:我們用一張圖表示構造函數和實例原型之間的關系好了構造函數和實例原型之間的關系我們已經梳理清楚了,那我們怎么表示實例與實例原型,也就是或者和之間的關系呢。 開篇: 在Brendan Eich大神為JavaScript設計面向對象系統的時候,借鑒了Self 和Smalltalk這兩門基于原型的語言,之所以選擇基于原型的面向對象系統,并不是因為時間匆忙,它設計起來相對簡單,而是因為從一開始B...
摘要:上圖中的在原型繼承稱作構造器。構造器就是一個普通的函數,但是將操作符用到構造器上時,它會執行一個叫的過程。從第條可以看到,構造器生成的對象的屬性會指向構造器的值,這就是我們構造原型鏈的關鍵。 基于類的繼承是大多數人所熟悉的,也是比較容易理解的。當我們形成類型繼承的思維定勢后,再次接觸原型繼承可能會覺得有些奇怪并難以理解。你更可能會吐槽,原型繼承根本就不能叫做繼承,一點都不面向對象。本人...
摘要:上圖中的在原型繼承稱作構造器。構造器就是一個普通的函數,但是將操作符用到構造器上時,它會執行一個叫的過程。從第條可以看到,構造器生成的對象的屬性會指向構造器的值,這就是我們構造原型鏈的關鍵。 基于類的繼承是大多數人所熟悉的,也是比較容易理解的。當我們形成類型繼承的思維定勢后,再次接觸原型繼承可能會覺得有些奇怪并難以理解。你更可能會吐槽,原型繼承根本就不能叫做繼承,一點都不面向對象。本人...
閱讀 3025·2021-11-24 10:21
閱讀 1596·2021-10-11 10:57
閱讀 2811·2021-09-22 15:24
閱讀 2667·2021-09-22 14:58
閱讀 2334·2019-08-30 13:16
閱讀 3483·2019-08-29 13:05
閱讀 3417·2019-08-29 12:14
閱讀 3452·2019-08-27 10:55