




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、Java從菜鳥到高手之設(shè)計模式設(shè)計模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計 經(jīng)驗的總結(jié)。使用設(shè)計模式是為了可重用代碼、 讓代碼更容易被他人理解、 保證代碼可靠性。 毫無疑問,設(shè)計模式于己于他人于系統(tǒng)都是多贏的,設(shè)計模式使代碼編制真正工程化,設(shè)計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理的運用設(shè)計模式可以完美 的解決很多問題,每種模式在現(xiàn)在中都有相應(yīng)的原理來與之對應(yīng),每一個模式描述了一個在我們周圍不斷重復(fù)發(fā)生的問題, 以及該問題的核心解決方案, 這也是它能被廣泛應(yīng)用的原因。 我會以理論與實踐相結(jié)合的方式來進行本章的學(xué)習(xí),希望廣大
2、程序愛好者,學(xué)好設(shè)計模式, 做一個優(yōu)秀的軟件工程師!一、設(shè)計模式的分類總體來說設(shè)計模式分為三大類:創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。 結(jié)構(gòu)型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模 式、享元模式。行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責(zé)任鏈模式、 命令模式、備忘錄模式、狀態(tài)模式、訪問者模式、中介者模式、解釋器模式。其實還有兩類:并發(fā)型模式和線程池模式。用一個圖片來整體描述一下:二、設(shè)計模式的六大原則1、開閉原則(Open Close Principle )開閉原則就是說對擴展開放,對
3、修改關(guān)閉。在程序需要進行拓展的時候,不能去修改原有的 代碼,實現(xiàn)一個熱插拔的效果。所以一句話概括就是:為了使程序的擴展性好,易于維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類,后面的具體設(shè)計中我們會提到這 點。2、里氏代換原則(Liskov Substitution Principle )里氏代換原則(Liskov Substitution Principle LSP)面向?qū)ο笤O(shè)計的基本原則之一。里氏代換原則中說,任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn)。LSP是繼承復(fù)用的基石,只有當(dāng)衍生類可以替換掉基類,軟件單位的功能不受到影響時,基類才能真正被復(fù)用,而衍生類也能夠在基類的基礎(chǔ)上增
4、加新的行為。里氏代換原則是對 “開-閉”原則的補充。實現(xiàn)“開-閉”原則的關(guān)鍵步驟就是抽象化。而基類與子類的繼承關(guān)系就是抽象化的具體實現(xiàn),所以里氏代換原則是對實現(xiàn)抽象化的具體步驟的規(guī)范。一一From Baidu百科3、 依賴倒轉(zhuǎn)原則( Dependence Inversion Principle)這個是開閉原則的基礎(chǔ),具體內(nèi)容:真對接口編程,依賴于抽象而不依賴于具體。4、接口隔離原則(In terface Segregation Prin ciple)這個原則的意思是: 使用多個隔離的接口, 比使用單個接口要好。 還是一個降低類之間的耦 合度的意思,從這兒我們看出,其實設(shè)計模式就是一個軟件的設(shè)計
5、思想,從大型軟件架構(gòu)出發(fā),為了升級和維護方便。所以上文中多次出現(xiàn):降低依賴,降低耦合。5、 迪米特法則(最少知道原則)(Demeter Principle)為什么叫最少知道原則,就是說:一個實體應(yīng)當(dāng)盡量少的與其他實體之間發(fā)生相互作用,使得系統(tǒng)功能模塊相對獨立。6、合成復(fù)用原則(Composite Reuse Principle)原則是盡量使用合成/聚合的方式,而不是使用繼承。三、Java的23中設(shè)計模式從這一塊開始,我們詳細介紹Java中23種設(shè)計模式的概念,應(yīng)用場景等情況,并結(jié)合他們的特點及設(shè)計模式的原則進行分析。I、工廠方法模式(Factory Method)工廠方法模式分為三種:II、普
6、通工廠模式,就是建立一個工廠類,對實現(xiàn)了同一接口的一些類進行實例的創(chuàng)建。首 先看下關(guān)系圖:SfriKiFacTory*ptoduce()Sender*Send(舉例如下:(我們舉一個發(fā)送郵件和短信的例子)首先,創(chuàng)建二者的共同接口:java view plain copypublic interface Sender public void Send();其次,創(chuàng)建實現(xiàn)類:java view plain copypublic class MailSender implements Sender Overridepublic void Send() System.out.println("
7、;this is mailsender!");java view plain copypublic class SmsSender implements Sender Overridepublic void Send() System.out.println("this is sms sender!");最后,建工廠類:java view plain copypublic class SendFactory public Sender produce(String type) if ("mail".equals(type) return new
8、 MailSender(); else if ("sms".equals(type) return new SmsSender(); else System.out.println(" 請輸入正確的類型 !"); return null;我們來測試下:java view plain copypublic class FactoryTest public static void main(String args) SendFactory factory = new SendFactory(); Sender sender = duce
9、("sms"); sender.Send();輸出:this is sms sen der!22、多個工廠方法模式, 是對普通工廠方法模式的改進, 在普通工廠方法模式中, 如果傳遞 的字符串出錯,則不能正確創(chuàng)建對象, 而多個工廠方法模式是提供多個工廠方法, 分別創(chuàng)建 對象。關(guān)系圖:*Send()MailS*n<?crSwdQ將上面的代碼做下修改,改動下java view pla in copypublic class Sen dFactory SendFactory類就行,如下:public Sen der produceMail() return new MailS
10、e nder();public Sen der produceSms() return new SmsSe nder();測試類如下:java view pla in copypublic class FactoryTest public static void main(String args) Sen dFactory factory = new Sen dFactory();Sen der sen der = duceMail(); sen der.Se nd();輸出:this is mailse nder!不需要創(chuàng)建實例,33、靜態(tài)工廠方法模式,將上面的多個工廠
11、方法模式里的方法置為靜態(tài)的, 直接調(diào)用即可。java view pla in copypublic class Sen dFactory public static Sen der produceMail() return new MailSe nder();public static Sen der produceSms() return new SmsSe nder();java view pla in copypublic class FactoryTest public static void main(String args) Sen der sen der = Sen dFacto
12、duceMail();sen der.Se nd();輸出:this is mailse nder!總體來說,工廠模式適合:凡是出現(xiàn)了大量的產(chǎn)品需要創(chuàng)建,并且具有共同的接口時,可以通過工廠方法模式進行創(chuàng)建。在以上的三種模式中,第一種如果傳入的字符串有誤,不能正確創(chuàng)建對象,第三種相對于第二種,不需要實例化工廠類,所以,大多數(shù)情況下,我們會選 用第三種靜態(tài)工廠方法模式。2、抽象工廠模式(Abstract Factory)工廠方法模式有一個問題就是,類的創(chuàng)建依賴工廠類,也就是說,如果想要拓展程序,必須對工廠類進行修改,這違背了閉包原則,所以,從設(shè)計角度考慮,有一定的問題,如何解決? 就用
13、到抽象工廠模式,創(chuàng)建多個工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類 就可以了,不需要修改之前的代碼。因為抽象工廠不太好理解,我們先看看圖,然后就和代碼,就比較容易理解。PwvidAfjS4內(nèi)d榊*prt)(iuCflO*Sen<IO請看例子:java view pla in copy public in terface Sen der public void Sen d();兩個實現(xiàn)類:java view plain copypublic class MailSender implements Sender Override public void Send() System.
14、out.println("this is mailsender!");java view plain copypublic class SmsSender implements Sender Override public void Send() System.out.println("this is sms sender!"); 兩個工廠類:java view plain copypublic class SendMailFactory implements Provider Overridepublic Sender produce()return
15、new MailSender();java view plain copypublic class SendSmsFactory implements ProviderOverridepublic Sender produce() return new SmsSender();在提供一個接口:java view plain copypublic interface Provider public Sender produce();測試類:java view plain copypublic class Test public static void main(String args) Prov
16、ider provider = new SendMailFactory();Sender sender = duce(); sender.Send();其實這個模式的好處就是, 如果你現(xiàn)在想增加一個功能: 發(fā)及時信息, 則只需做一個實現(xiàn)類, 實現(xiàn) Sender 接口,同時做一個工廠類,實現(xiàn) Provider 接口,就 OK 了,無需去改動現(xiàn)成的 代碼。這樣做,拓展性較好!3、單例模式( Singleton )單例對象 ( Singleton )是一種常用的設(shè)計模式。 在 Java 應(yīng)用中, 單例對象能保證在一個 JVM 中,該對象只有一個實例存在。這樣的模式有幾個好處:
17、1、某些類創(chuàng)建比較頻繁,對于一些大型的對象,這是一筆很大的系統(tǒng)開銷。2、省去了 new 操作符,降低了系統(tǒng)內(nèi)存的使用頻率,減輕GC 壓力。3、有些類如交易所的核心交易引擎,控制著交易流程,如果該類可以創(chuàng)建多個的話,系統(tǒng) 完全亂了。(比如一個軍隊出現(xiàn)了多個司令員同時指揮, 肯定會亂成一團) ,所以只有使用單 例模式,才能保證核心交易服務(wù)器獨立控制整個流程。首先我們寫一個簡單的單例類:java view plain copy public class Singleton /* 持有私有靜態(tài)實例,防止被引用,此處賦值為null ,目的是實現(xiàn)延遲加載 */private static Singleto
18、n instance = null;/* 私有構(gòu)造方法,防止被實例化 */private Singleton() /* 靜態(tài)工程方法,創(chuàng)建實例 */public static Singleton getInstance() if (instance = null) instance = new Singleton();return instance;/* 如果該對象被用于序列化,可以保證對象在序列化前后保持一致 */ public Object readResolve() return instance;這個類可以滿足基本要求, 但是, 像這樣毫無線程安全保護的類, 如果我們把它放入多線程 的
19、環(huán)境下,肯定就會出現(xiàn)問題了,如何解決?我們首先會想到對 getInstance 方法加 synchronized 關(guān)鍵字,如下:java view plain copy public static synchronized Singleton getInstance() if (instance = null) instance = new Singleton();return instance; 但是, synchronized 關(guān)鍵字鎖住的是這個對象,這樣的用法,在性能上會有所下降,因為每 次調(diào)用getlnstance(),都要對對象上鎖,事實上,只有在第一次創(chuàng)建對象的時候需要加鎖, 之后
20、就不需要了,所以,這個地方需要改進。我們改成下面這個: java view plain copypublic static Singleton getlnstance() if (instance = null) synchronized (instance) if (instance = null) instance = new Singleton();return instance; 似乎解決了之前提到的問題, 將 synchronized 關(guān)鍵字加在了內(nèi)部, 也就是說當(dāng)調(diào)用的時候是 不需要加鎖的,只有在instanee為null,并創(chuàng)建對象的時候才需要加鎖,性能有一定的提升。但是,這樣的
21、情況,還是有可能有問題的,看下面的情況:在Java 指令中創(chuàng)建對象和賦值操作是分開進行的,也就是說 instance = new Singleton(); 語句是分兩步執(zhí)行的。但是 JVM 并 不保證這兩個操作的先后順序, 也就是說有可能 JVM 會為新的 Singleton 實例分配空間, 然 后直接賦值給 instance 成員,然后再去初始化這個 Singleton 實例。這樣就可能出錯了,我 們以 A、B 兩個線程為例: a>A、 B 線程同時進入了第一個 if 判斷b>A 首先進入 synchronized 塊,由于 instance 為 null ,所以它執(zhí)行 inst
22、ance = new Singleton(); c>由于JVM內(nèi)部的優(yōu)化機制,JVM先畫出了一些分配給Singleton實例的空白內(nèi)存,并賦值給 instance 成員(注意此時 JVM 沒有開始初始化這個實例) ,然后 A 離開了 synchronized 塊。d>B進入synchronized塊,由于instanee此時不是 null,因此它馬上離開了synchronized塊并將結(jié)果返回給調(diào)用該方法的程序。e>此時B線程打算使用Singleton實例,卻發(fā)現(xiàn)它沒有被初始化,于是錯誤發(fā)生了。 所以程序還是有可能發(fā)生錯誤,其實程序在運行過程是很復(fù)雜的,從這點我們就可以看出,
23、 尤其是在寫多線程環(huán)境下的程序更有難度,有挑戰(zhàn)性。我們對該程序做進一步優(yōu)化:java view plain copy private static class SingletonFactoryprivate static Singleton instance = new Singleton();public static Singleton getlnstance()return SingletonFactory.instance;實際情況是,單例模式使用內(nèi)部類來維護單例的實現(xiàn), JVM 內(nèi)部的機制能夠保證當(dāng)一個類 被加載的時候,這個類的加載過程是線程互斥的。這樣當(dāng)我們第一次調(diào)用 getIns
24、tance 的時 候, JVM 能夠幫我們保證 instance 只被創(chuàng)建一次, 并且會保證把賦值給 instance 的內(nèi)存初始 化完畢, 這樣我們就不用擔(dān)心上面的問題。 同時該方法也只會在第一次調(diào)用的時候使用互斥 機制,這樣就解決了低性能問題。這樣我們暫時總結(jié)一個完美的單例模式:java view plain copy public class Singleton /* 私有構(gòu)造方法,防止被實例化 */private Singleton() /* 此處使用一個內(nèi)部類來維護單例 */private static class SingletonFactory private static Si
25、ngleton instance = new Singleton();/* 獲取實例 */public static Singleton getInstance() return SingletonFactory.instance;/* 如果該對象被用于序列化,可以保證對象在序列化前后保持一致 */public Object readResolve() return getInstance();其實說它完美, 也不一定, 如果在構(gòu)造函數(shù)中拋出異常, 實例將永遠得不到創(chuàng)建, 也會出錯。 所以說, 十分完美的東西是沒有的, 我們只能根據(jù)實際情況, 選擇最適合自己應(yīng)用場景的實 現(xiàn)方法。也有人這樣實現(xiàn)
26、:因為我們只需要在創(chuàng)建類的時候進行同步,所以只要將創(chuàng)建和getlnstance()分開,單獨為創(chuàng)建加synchronized關(guān)鍵字,也是可以的:java ew plain copypublic class SingletonTest private static SingletonTest instance = null;private SingletonTest() private static synchronized void synclnit() if (instance = null) instance = new SingletonTest(); public static Sin
27、gletonTest getInstance() if (instance = null) syncInit();return instance; 考慮性能的話,整個程序只需創(chuàng)建一次實例,所以性能也不會有什么影響。 補充:采用 " 影子實例 " 的辦法為單例對象的屬性同步更新java view plain copy public class SingletonTest private static SingletonTest instance = null;private Vector properties = null;public Vector getPropertie
28、s() return properties;private SingletonTest() private static synchronized void syncInit() if (instance = null) instance = new SingletonTest();public static SingletonTest getInstance() if (instance = null) syncInit();return instance;public void updateProperties() SingletonTest shadow = new SingletonT
29、est(); properties = shadow.getProperties(); 通過單例模式的學(xué)習(xí)告訴我們: 1、單例模式理解起來簡單,但是具體實現(xiàn)起來還是有一定的難度。2、 synchronized 關(guān)鍵字鎖定的是對象,在用的時候,一定要在恰當(dāng)?shù)牡胤绞褂?注意需要 使用鎖的對象和過程,可能有的時候并不是整個對象及整個過程都需要鎖) 。 到這兒,單例模式基本已經(jīng)講完了, 結(jié)尾處, 筆者突然想到另一個問題,就是采用類的靜態(tài) 方法,實現(xiàn)單例模式的效果,也是可行的,此處二者有什么不同? 首先,靜態(tài)類不能實現(xiàn)接口。 (從類的角度說是可以的,但是那樣就破壞了靜態(tài)了。因為接 口中不允許有 stat
30、ic 修飾的方法,所以即使實現(xiàn)了也是非靜態(tài)的) 其次, 單例可以被延遲初始化, 靜態(tài)類一般在第一次加載是初始化。之所以延遲加載, 是因 為有些類比較龐大,所以延遲加載有助于提升性能。再次,單例類可以被繼承,他的方法可以被覆寫。但是靜態(tài)類內(nèi)部方法都是static,無法被覆寫。最后一點,單例類比較靈活,畢竟從實現(xiàn)上只是一個普通的Java 類,只要滿足單例的基本需求,你可以在里面隨心所欲的實現(xiàn)一些其它功能,但是靜態(tài)類不行。從上面這些概括中, 基本可以看出二者的區(qū)別,但是,從另一方面講,我們上面最后實現(xiàn)的那個單例模式,內(nèi)部 就是用一個靜態(tài)類來實現(xiàn)的, 所以,二者有很大的關(guān)聯(lián), 只是我們考慮問題的層面不
31、同罷了。兩種思想的結(jié)合,才能造就出完美的解決方案,就像HashMap采用數(shù)組+鏈表來實現(xiàn)一樣,其實生活中很多事情都是這樣, 單用不同的方法來處理問題, 總是有優(yōu)點也有缺點, 最完美 的方法是,結(jié)合各個方法的優(yōu)點,才能最好的解決問題!4、建造者模式( Builder ) 工廠類模式提供的是創(chuàng)建單個類的模式,而建造者模式則是將各種產(chǎn)品集中起來進行管理, 用來創(chuàng)建復(fù)合對象, 所謂復(fù)合對象就是指某個類具有不同的屬性, 其實建造者模式就是前面 抽象工廠模式和最后的Test結(jié)合起來得到的。我們看一下代碼:還和前面一樣,一個Sender接口,兩個實現(xiàn)類 MailSender和SmsSender。最后,建造者
32、類如下:java view plain copypublic class Builder private List<Sender> list = new ArrayList<Sender>();public void produceMailSender(int count)for(int i=0; i<count; i+)list.add(new MailSender();public void produceSmsSender(int count)for(int i=0; i<count; i+)list.add(new SmsSender();測試類:j
33、ava view plain copypublic class Test public static void main(String args) Builder builder = new Builder(); duceMailSender(10); 從這點看出,建造者模式將很多功能集成到一個類里,這個類可以創(chuàng)造出比較復(fù)雜的東西。 所以與工程模式的區(qū)別就是: 工廠模式關(guān)注的是創(chuàng)建單個產(chǎn)品, 而建造者模式則關(guān)注創(chuàng)建符 合對象,多個部分。因此,是選擇工廠模式還是建造者模式,依實際情況而定。5、原型模式( Prototype) 原型模式雖然是創(chuàng)建型的模式, 但是與工程模式?jīng)]有
34、關(guān)系, 從名字即可看出, 該模式的思想 就是將一個對象作為原型,對其進行復(fù)制、克隆,產(chǎn)生一個和原對象類似的新對象。 本小結(jié) 會通過對象的復(fù)制,進行講解。在 Java 中,復(fù)制對象是通過 clone() 實現(xiàn)的,先創(chuàng)建一個原 型類:,丿 7 java view plain copy public class Prototype implements Cloneable public Object clone() throws CloneNotSupportedException Prototype proto = (Prototype) super.clone();return proto;很簡單,一個原型類,只需要實現(xiàn) Cloneable 接口,覆寫 clone 方法,此處 clone 方法可以改 成任意的名稱, 因為 Cloneable 接口是個空接口, 你可以任意定義實現(xiàn)類的方法名, 如 cloneA 或者cloneB ,因為此處的重點是 super.clone()這句話,super.clone()調(diào)用的是 Object的clone() 方法,而在Object類中,clone()是native的,具體怎么實現(xiàn),我會在另一篇文章中,關(guān)于解 讀 Java 中本地方法的調(diào)用,此處不再深究。在這兒,我將結(jié)合對象的淺復(fù)制和深復(fù)制來說 一下,首先需要了解對象深、
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 益智課題申報書范文
- 和老外合同范例
- 課題申報書范例范文
- 代替舊合同新合同范例
- 教育范式 課題申報書
- 原液供貨合同范本
- 課題申報書質(zhì)問點
- 合伙買車經(jīng)營合同范本
- 牡丹江課題立項申報書
- 買房付款寫合同范本
- 營銷部安全生產(chǎn)責(zé)任制
- 【講座】高三英語高效二輪備考講座課件
- 2022-2023學(xué)年遼寧省鞍山市普通高中高一年級下冊學(xué)期第一次月考數(shù)學(xué)(A卷)試題【含答案】
- 2022年安徽醫(yī)科大學(xué)第一附屬醫(yī)院臨床醫(yī)技、護理、管理崗位招聘187人筆試備考題庫及答案解析
- 弟子規(guī)42+用人物須明求+教案
- 微電網(wǎng)-儲能電池catl pet80ah電芯規(guī)格書
- GB/T 4209-2022工業(yè)硅酸鈉
- 2023年江蘇農(nóng)林職業(yè)技術(shù)學(xué)院高職單招(數(shù)學(xué))試題庫含答案解析
- GB/T 39242-2020無損檢測超聲檢測靈敏度和范圍設(shè)定
- GB/T 32271-2015電梯能量回饋裝置
- GB/T 18775-2009電梯、自動扶梯和自動人行道維修規(guī)范
評論
0/150
提交評論