Java設計模式_第1頁
Java設計模式_第2頁
Java設計模式_第3頁
Java設計模式_第4頁
Java設計模式_第5頁
已閱讀5頁,還剩36頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、公司名稱設計模式(Design Patterns)可復用面向對象軟件基礎Jiaxing Zhang日期設計模式(Design Patterns) 可復用面向對象軟件的基礎設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式于己于他人于系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不

2、斷重復發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。一、設計模式的分類總體來說設計模式分為三大類:創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。二、設計模式的六大原則1. 開閉原則(Open Close Principle)開閉原則就是說對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有

3、的代碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程序的擴展性好,易于維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類,后面的具體設計中我們會提到這點。2. 里氏代換原則(Liskov Substitution Principle)里氏代換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。 LSP是繼承復用的基石,只有當衍生類可以替換掉基類,軟件單位的功能不受到影響時,基類才能真正被復用,而衍生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對“開-閉”原則的補充。

4、實現“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關系就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規范。 From Baidu 百科3. 依賴倒轉原則(Dependence Inversion Principle)這個是開閉原則的基礎,具體內容:真對接口編程,依賴于抽象而不依賴于具體。4. 接口隔離原則(Interface Segregation Principle)這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。還是一個降低類之間的耦合度的意思,從這兒我們看出,其實設計模式就是一個軟件的設計思想,從大型軟件架構出發,為了升級和維護方便。所以上文中多次出現

5、:降低依賴,降低耦合。5. 迪米特法則(最少知道原則)(Demeter Principle)為什么叫最少知道原則,就是說:一個實體應當盡量少的與其他實體之間發生相互作用,使得系統功能模塊相對獨立。6. 合成復用原則(Composite Reuse Principle)原則是盡量使用合成/聚合的方式,而不是使用繼承。三、Java的23中設計模式從這一塊開始,我們詳細介紹Java中23種設計模式的概念,應用場景等情況,并結合他們的特點及設計模式的原則進行分析。1.工廠方法模式(Factory Method)工廠方法模式經典案例:迭代器Iterator工廠方法模式分為三種:1. 普通工廠模式,就是建

6、立一個工廠類,對實現了同一接口的一些類進行實例的創建。例程(發送郵件與接收郵件):public interface Sender public void Send(); 其次,創建實現類:public class MailSender implements Sender Override public void Send() System.out.println(this is mailsender!); public class SmsSender implements Sender Override public void Send() System.out.println(this is

7、 sms sender!); 最后,建工廠類:public class SendFactory public Sender produce(String type) if (mail.equals(type) return new MailSender(); else if (sms.equals(type) return new SmsSender(); else System.out.println(請輸入正確的類型!); return null; 我們來測試下:public class FactoryTest public static void main(String args) Se

8、ndFactory factory = new SendFactory(); Sender sender = duce(sms); sender.Send(); 2. 多個工廠方法模式,是對普通工廠方法模式的改進,在普通工廠方法模式中,如果傳遞的字符串出錯,則不能正確創建對象,而多個工廠方法模式是提供多個工廠方法,分別創建對象。修改上面代碼的SendFactory:public class SendFactory public Sender produceMail() return new MailSender(); public Sender produceSms()

9、return new SmsSender(); 3. 靜態工廠方法模式,將上面的多個工廠方法模式里的方法置為靜態的,不需要創建實例,直接調用即可。public class SendFactory public static Sender produceMail() return new MailSender(); public static Sender produceSms() return new SmsSender(); 工廠方法模式總結:總體來說,工廠模式適合:凡是出現了大量的產品需要創建,并且具有公共的接口時,可以通過工廠方法模式進行創建,在以上三種設計模式中,第一種傳入的字符串如果

10、有誤,那么就不能正確創建對象,第三種相對于第二種,不需要實例化工廠類,所以在大多數情況下,我們會選用第三種靜態工廠方法模式2.抽象工廠模式(Abstract Factory)工廠方法模式有一個問題就是,類的創建依賴工廠類,也就是說,如果想要拓展程序,必須對工廠類進行修改,這違背了閉包原則,所以,從設計角度考慮,有一定的問題,如何解決?就用到抽象工廠模式,創建多個工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了,不需要修改之前的代碼。首先創建Sender接口:public interface Sender abstract public void send();再創建兩個Sender

11、接口的實現類public class MailSender implements Sender Override public void send() System.out.println(This is a Mail Sender!); public class SmsSender implements Sender Override public void send() System.out.println(This is a SMS Sender!); 接著創建工廠類接口,提供provider方法:public interface Provider abstract public Sen

12、der produce();接著創建兩個工廠類的實現類public class SendMailFactory implements Provider Override public Sender produce() return new MailSender(); public class SendSmsFactory implements Provider Override public Sender produce() return new SmsSender(); 這樣定義的話擴展性好,如果想實現其他的發送方式只需要在提供一個工廠類,無需對原來的代碼進行修改3.單例模式(Singlet

13、on)單例對象(Singleton)是一種常用的設計模式。在Java應用中,單例對象能保證在一個JVM中,該對象只有一個實例存在。這樣的模式有幾個好處:1. 某些類創建比較頻繁,對于一些大型的對象,這是一筆很大的系統開銷。2. 省去了new操作符,降低了系統內存的使用頻率,減輕GC壓力。3. 有些類如交易所的核心交易引擎,控制著交易流程,如果該類可以創建多個的話,系統完全亂了。(比如一個軍隊出現了多個司令員同時指揮,肯定會亂成一團),所以只有使用單例模式,才能保證核心交易服務器獨立控制整個流程。首先我們寫一個簡單的單例類:java view plain copypublic class Sin

14、gleton /* 持有私有靜態實例,防止被引用,此處賦值為null,目的是實現延遲加載 */ private static Singleton instance = null; /* 私有構造方法,防止被實例化 */ private Singleton() /* 靜態工程方法,創建實例 */ public static Singleton getInstance() if (instance = null) instance = new Singleton(); return instance; /* 如果該對象被用于序列化,可以保證對象在序列化前后保持一致 */ public Object

15、 readResolve() return instance; 這個類可以滿足基本要求,但是,像這樣毫無線程安全保護的類,如果我們把它放入多線程的環境下,肯定就會出現問題了,如何解決?我們首先會想到對getInstance方法加synchronized關鍵字,如下:java view plain copypublic static synchronized Singleton getInstance() if (instance = null) instance = new Singleton(); return instance; 但是,synchronized關鍵字鎖住的是這個對象,這樣的

16、用法,在性能上會有所下降,因為每次調用getInstance(),都要對對象上鎖,事實上,只有在第一次創建對象的時候需要加鎖,之后就不需要了,所以,這個地方需要改進。我們改成下面這個:java view plain copypublic static Singleton getInstance() if (instance = null) synchronized (Singleton.class) if (instance = null) instance = new Singleton(); return instance; 似乎解決了之前提到的問題,將synchronized關鍵字加在了

17、內部,也就是說當調用的時候是不需要加鎖的,只有在instance為null,并創建對象的時候才需要加鎖,性能有一定的提升。但是,這樣的情況,還是有可能有問題的,看下面的情況:在Java指令中創建對象和賦值操作是分開進行的,也就是說instance = new Singleton();語句是分兩步執行的。但是JVM并不保證這兩個操作的先后順序,也就是說有可能JVM會為新的Singleton實例分配空間,然后直接賦值給instance成員,然后再去初始化這個Singleton實例。這樣就可能出錯了,我們以A、B兩個線程為例:a) B線程同時進入了第一個if判斷b) A首先進入synchronize

18、d塊,由于instance為null,所以它執行instance = new Singleton();c) 由于JVM內部的優化機制,JVM先畫出了一些分配給Singleton實例的空白內存,并賦值給instance成員(注意此時JVM沒有開始初始化這個實例),然后A離開了synchronized塊。d) B進入synchronized塊,由于instance此時不是null,因此它馬上離開了synchronized塊并將結果返回給調用該方法的程序。e) 此時B線程打算使用Singleton實例,卻發現它沒有被初始化,于是錯誤發生了。f) 所以程序還是有可能發生錯誤,其實程序在運行過程是很復雜

19、的,從這點我們就可以看出,尤其是在寫多線程環境下的程序更有難度,有挑戰性。我們對該程序做進一步優化:java view plain copyprivate static class SingletonFactory private static Singleton instance = new Singleton(); public static Singleton getInstance() return SingletonFactory.instance; 實際情況是,單例模式使用內部類來維護單例的實現,JVM內部的機制能夠保證當一個類被加載的時候,這個類的加載過程是線程互斥的。這樣當我們

20、第一次調用getInstance的時候,JVM能夠幫我們保證instance只被創建一次,并且會保證把賦值給instance的內存初始化完畢,這樣我們就不用擔心上面的問題。同時該方法也只會在第一次調用的時候使用互斥機制,這樣就解決了低性能問題。這樣我們暫時總結一個完美的單例模式:java view plain copypublic class Singleton /* 私有構造方法,防止被實例化 */ private Singleton() /* 此處使用一個內部類來維護單例 */ private static class SingletonFactory private static Sin

21、gleton instance = new Singleton(); /* 獲取實例 */ public static Singleton getInstance() return SingletonFactory.instance; /* 如果該對象被用于序列化,可以保證對象在序列化前后保持一致 */ public Object readResolve() return getInstance(); 其實說它完美,也不一定,如果在構造函數中拋出異常,實例將永遠得不到創建,也會出錯。所以說,十分完美的東西是沒有的,我們只能根據實際情況,選擇最適合自己應用場景的實現方法。也有人這樣實現:因為我們

22、只需要在創建類的時候進行同步,所以只要將創建和getInstance()分開,單獨為創建加synchronized關鍵字,也是可以的:java view plain copypublic class SingletonTest private static SingletonTest instance = null; private SingletonTest() private static synchronized void syncInit() if (instance = null) instance = new SingletonTest(); public static Singl

23、etonTest getInstance() if (instance = null) syncInit(); return instance; 考慮性能的話,整個程序只需創建一次實例,所以性能也不會有什么影響。補充:采用影子實例的辦法為單例對象的屬性同步更新java view plain copypublic class SingletonTest private static SingletonTest instance = null; private Vector properties = null; public Vector getProperties() return proper

24、ties; 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 SingletonTest(); proper

25、ties = shadow.getProperties(); 通過單例模式的學習告訴我們:1、單例模式理解起來簡單,但是具體實現起來還是有一定的難度。2、synchronized關鍵字鎖定的是對象,在用的時候,一定要在恰當的地方使用(注意需要使用鎖的對象和過程,可能有的時候并不是整個對象及整個過程都需要鎖)。到這兒,單例模式基本已經講完了,結尾處,筆者突然想到另一個問題,就是采用類的靜態方法,實現單例模式的效果,也是可行的,此處二者有什么不同?首先,靜態類不能實現接口。(從類的角度說是可以的,但是那樣就破壞了靜態了。因為接口中不允許有static修飾的方法,所以即使實現了也是非靜態的)其次,單

26、例可以被延遲初始化,靜態類一般在第一次加載是初始化。之所以延遲加載,是因為有些類比較龐大,所以延遲加載有助于提升性能。再次,單例類可以被繼承,他的方法可以被覆寫。但是靜態類內部方法都是static,無法被覆寫。最后一點,單例類比較靈活,畢竟從實現上只是一個普通的Java類,只要滿足單例的基本需求,你可以在里面隨心所欲的實現一些其它功能,但是靜態類不行。從上面這些概括中,基本可以看出二者的區別,但是,從另一方面講,我們上面最后實現的那個單例模式,內部就是用一個靜態類來實現的,所以,二者有很大的關聯,只是我們考慮問題的層面不同罷了。兩種思想的結合,才能造就出完美的解決方案,就像HashMap采用數

27、組+鏈表來實現一樣,其實生活中很多事情都是這樣,單用不同的方法來處理問題,總是有優點也有缺點,最完美的方法是,結合各個方法的優點,才能最好的解決問題!4.建造者模式(Builder)建造者模式經典案例:JavaMail建造者模式:是將一個復雜的對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式通常包括下面幾個角色:1. builder:給出一個抽象接口,以規范產品對象的各個組成成分的建造。這個接口規定要實現復雜對象的哪些部分的創建,并不涉及具體的對象部件的創建。2. ConcreteBuilder:實現Builder接口,針對不同的商業邏輯,具體化復雜對象的各部分的創建

28、。 在建造過程完成后,提供產品的實例。3. Director:調用具體建造者來創建復雜對象的各個部分,在指導者中不涉及具體產品的信息,只負責保證對象各部分完整創建或按某種順序創建。4. Product:要創建的復雜對象。例程:Builder:public interface Builder void buildPartA(); void buildPartB(); void buildPartC(); void buildPartD(); Product getProduct();ConcreteBuilder:public class ConcreteBuilder implements B

29、uilder private Product product = new Product(); Override public void buildPartA() product.setPartA(PartA); Override public void buildPartB() product.setPartB(PartB); Override public void buildPartC() product.setPartC(PartC); Override public void buildPartD() product.setPartD(PartD); Override public

30、Product getProduct() return product; Director:public class Director private Builder builder; public Director(Builder builder) this.builder = builder; public void construct() builder.buildPartA(); builder.buildPartB(); builder.buildPartC(); builder.buildPartD(); Productpublic class Product public Str

31、ing partA; public String partB; public String partC; public String partD; public String getPartA() return partA; public void setPartA(String partA) this.partA = partA; . Override public String toString() return Product + partA= + partA + + , partB= + partB + + , partC= + partC + + , partD= + partD +

32、 + ; 測試類:public void test_1() ConcreteBuilder builder = new ConcreteBuilder();/指定需求的產品 Director director = new Director(builder);/創建建造者 director.construct();/建造者根據需求來構建產品 Product product = builder.getProduct(); System.out.println(product);5.原型模式(Prototype)原型模式雖然是創建型的模式,但是與工程模式沒有關系,從名字即可看出,該模式的思想就是將一

33、個對象作為原型,對其進行復制、克隆,產生一個和原對象類似的新對象。本小結會通過對象的復制,進行講解。在Java中,復制對象是通過clone()實現的,先創建一個原型類:java view plain copypublic class Prototype implements Cloneable public Object clone() throws CloneNotSupportedException Prototype proto = (Prototype) super.clone(); return proto; 很簡單,一個原型類,只需要實現Cloneable接口,覆寫clone方法,

34、此處clone方法可以改成任意的名稱,因為Cloneable接口是個空接口,你可以任意定義實現類的方法名,如cloneA或者cloneB,因為此處的重點是super.clone()這句話,super.clone()調用的是Object的clone()方法,而在Object類中,clone()是native的,具體怎么實現,我會在另一篇文章中,關于解讀Java中本地方法的調用,此處不再深究。在這兒,我將結合對象的淺復制和深復制來說一下,首先需要了解對象深、淺復制的概念:l 淺復制:將一個對象復制后,基本數據類型的變量都會重新創建,而引用類型,指向的還是原對象所指向的。l 深復制:將一個對象復制后

35、,不論是基本數據類型還有引用類型,都是重新創建的。簡單來說,就是深復制進行了完全徹底的復制,而淺復制不徹底。此處,寫一個深淺復制的例子:java view plain copypublic class Prototype implements Cloneable, Serializable private static final long serialVersionUID = 1L; private String string; private SerializableObject obj; /* 淺復制 */ public Object clone() throws CloneNotSup

36、portedException Prototype proto = (Prototype) super.clone(); return proto; /* 深復制 */ public Object deepClone() throws IOException, ClassNotFoundException /* 寫入當前對象的二進制流 */ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(t

37、his); /* 讀出二進制流產生的新對象 */ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray(); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); public String getString() return string; public void setString(String string) this.string = string; public SerializableObject g

38、etObj() return obj; public void setObj(SerializableObject obj) this.obj = obj; class SerializableObject implements Serializable private static final long serialVersionUID = 1L; 要實現深復制,需要采用流的形式讀入當前對象的二進制輸入,再寫出二進制數據對應的對象。6.適配器模式(Adapter) 適配器模式將某個類的接口轉換成客戶端期望的另一個接口表示,目的是消除由于接口不匹配所造成的類的兼容性問題。主要分為三類:類的適配

39、器模式、對象的適配器模式、接口的適配器模式。類的適配器模式核心思想就是:有一個Source類,擁有一個方法,待適配,目標接口是Targetable,通過Adapter類,將Source的功能擴展到Targetable里,看代碼:java view plain copypublic class Source public void method1() System.out.println(this is original method!); java view plain copypublic interface Targetable /* 與原類中的方法相同 */ public void me

40、thod1(); /* 新類的方法 */ public void method2(); java view plain copypublic class Adapter extends Source implements Targetable Override public void method2() System.out.println(this is the targetable method!); Adapter類繼承Source類,實現Targetable接口,下面是測試類:java view plain copypublic class AdapterTest public sta

41、tic void main(String args) Targetable target = new Adapter(); target.method1(); target.method2(); 輸出:this is original method!this is the targetable method!這樣Targetable接口的實現類就具有了Source類的功能。對象的適配器模式基本思路和類的適配器模式相同,只是將Adapter類作修改,這次不繼承Source類,而是持有Source類的實例,以達到解決兼容性的問題。只需要修改Adapter類的源碼即可:public interfac

42、e Targetable public void method1(); public void method2();java view plain copypublic class Wrapper implements Targetable private Source source; public Wrapper(Source source) super(); this.source = source; Override public void method2() System.out.println(this is the targetable method!); Override pub

43、lic void method1() source.method1(); 測試類public class AdapterTest public static void main(String args) Source source = new Source(); Targetable target = new Wrapper(source); target.method1(); target.method2(); 接口的適配器模式第三種適配器模式是接口的適配器模式,接口的適配器是這樣的:有時我們寫的一個接口中有多個抽象方法,當我們寫該接口的實現類時,必須實現該接口的所有方法,這明顯有時比較浪費

44、,因為并不是所有的方法都是我們需要的,有時只需要某一些,此處為了解決這個問題,我們引入了接口的適配器模式,借助于一個抽象類,該抽象類實現了該接口,實現了所有的方法,而我們不和原始的接口打交道,只和該抽象類取得聯系,所以我們寫一個類,繼承該抽象類,重寫我們需要的方法就行。 這個很好理解,在實際開發中,我們也常會遇到這種接口中定義了太多的方法,以致于有時我們在一些實現類中并不是都需要。看代碼:java view plain copypublic interface Sourceable public void method1(); public void method2(); 抽象類Wrapper

45、2:java view plain copypublic abstract class Wrapper2 implements Sourceable public void method1() public void method2() java view plain copypublic class SourceSub1 extends Wrapper2 public void method1() System.out.println(the sourceable interfaces first Sub1!); java view plain copypublic class Source

46、Sub2 extends Wrapper2 public void method2() System.out.println(the sourceable interfaces second Sub2!); java view plain copypublic class WrapperTest public static void main(String args) Sourceable source1 = new SourceSub1(); Sourceable source2 = new SourceSub2(); source1.method1(); source1.method2()

47、; source2.method1(); source2.method2(); 測試輸出:the sourceable interfaces first Sub1!the sourceable interfaces second Sub2!達到了我們的效果! 講了這么多,總結一下三種適配器模式的應用場景:l 類的適配器模式:當希望將一個類轉換成滿足另一個新接口的類時,可以使用類的適配器模式,創建一個新類,繼承原有的類,實現新的接口即可。l 對象的適配器模式:當希望將一個對象轉換成滿足另一個新接口的對象時,可以創建一個Wrapper類,持有原類的一個實例,在Wrapper類的方法中,調用實例的方法就行。l 接口的適配器模式:當不希望實現一個接口中所有的方法時,可以創建一個抽象類Wrapper,實現所有

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論