GOF设计模式(概念、原则、场景、优点、缺点、应用)
設(shè)計(jì)模式是軟件大師們根據(jù)多年來的軟件開發(fā)經(jīng)驗(yàn),對(duì)軟件開發(fā)領(lǐng)域包括合理復(fù)用、提高健壯性、減少BUG等各方面作的抽象總結(jié),不同的設(shè)計(jì)模式方法適合于不同的應(yīng)用場(chǎng)景,是匯結(jié)了他們最寶貴的經(jīng)驗(yàn)總結(jié)。最早的開發(fā)模式是1994年GOF四人共同完成的《Design Patterns - Elements of Reusable Object-Oriented Software》一書提及的23種經(jīng)典設(shè)計(jì)模式,至今仍是設(shè)計(jì)模式方面,經(jīng)典中的經(jīng)典著作。
?
一、設(shè)計(jì)模式的六大原則:
總原則-開閉原則
對(duì)擴(kuò)展開放,對(duì)修改封閉。在程序需要進(jìn)行拓展的時(shí)候,不能去修改原有的代碼,而是要擴(kuò)展原有代碼,實(shí)現(xiàn)一個(gè)熱插拔的效果。所以一句話概括就是:為了使程序的擴(kuò)展性好,易于維護(hù)和升級(jí)。
想要達(dá)到這樣的效果,我們需要使用接口和抽象類等,后面的具體設(shè)計(jì)中我們會(huì)提到這點(diǎn)。
1、單一職責(zé)原則
不要存在多于一個(gè)導(dǎo)致類變更的原因,也就是說每個(gè)類應(yīng)該實(shí)現(xiàn)單一的職責(zé),否則就應(yīng)該把類拆分。
2、里氏替換原則(Liskov Substitution Principle)
任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn)。里氏替換原則是繼承復(fù)用的基石,只有當(dāng)衍生類可以替換基類,軟件單位的功能不受到影響時(shí),基類才能真正被復(fù)用,而衍生類也能夠在基類的基礎(chǔ)上增加新的行為。
里氏代換原則是對(duì)“開-閉”原則的補(bǔ)充。實(shí)現(xiàn)“開閉”原則的關(guān)鍵步驟就是抽象化。而基類與子類的繼承關(guān)系就是抽象化的具體實(shí)現(xiàn),所以里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。里氏替換原則中,子類對(duì)父類的方法盡量不要重寫和重載。因?yàn)楦割惔砹硕x好的結(jié)構(gòu),通過這個(gè)規(guī)范的接口與外界交互,子類不應(yīng)該隨便破壞它。
3、依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)
面向接口編程,依賴于抽象而不依賴于具體。寫代碼時(shí)用到具體類時(shí),不與具體類交互,而與具體類的上層接口交互。
4、接口隔離原則(Interface Segregation Principle)
每個(gè)接口中不存在子類用不到卻必須實(shí)現(xiàn)的方法,如果不然,就要將接口拆分。使用多個(gè)隔離的接口,比使用單個(gè)接口(多個(gè)接口方法集合到一個(gè)的接口)要好。
5、迪米特法則(最少知道原則)(Demeter Principle)
一個(gè)類對(duì)自己依賴的類知道的越少越好。無論被依賴的類多么復(fù)雜,都應(yīng)該將邏輯封裝在方法的內(nèi)部,通過public方法提供給外部。這樣當(dāng)被依賴的類變化時(shí),才能最小的影響該類。
最少知道原則的另一個(gè)表達(dá)方式是:只與直接的朋友通信。類之間只要有耦合關(guān)系,就叫朋友關(guān)系。耦合分為依賴、關(guān)聯(lián)、聚合、組合等。我們稱出現(xiàn)為成員變量、方法參數(shù)、方法返回值中的類為直接朋友。局部變量、臨時(shí)變量則不是直接的朋友。我們要求陌生的類不要作為局部變量出現(xiàn)在類中。
6、合成復(fù)用原則(Composite Reuse Principle)
盡量首先使用合成/聚合的方式,而不是使用繼承,即組合優(yōu)于繼承.
?
按照類型,可分為3類:
1、創(chuàng)建型模式:抽象工廠、建造者模式、工廠方法、原型模式、單例模式;
創(chuàng)建型模式抽象了實(shí)例化的過程。創(chuàng)建性模式隱藏了這些類的實(shí)例是如何被創(chuàng)建和放在一起,整個(gè)系統(tǒng)關(guān)于這些對(duì)象所知道的是由抽象類所定義的接口。這樣,創(chuàng)建性模式在創(chuàng)建了什么、誰創(chuàng)建它、她是怎么被創(chuàng)建的、以及何時(shí)創(chuàng)建方面提供了靈活性。創(chuàng)建相應(yīng)數(shù)目的原型并克隆她們通常比每次用適合的狀態(tài)手工實(shí)例化該類更方便。
2、結(jié)構(gòu)型模式:適配器模式、橋接模式、組合模式、裝飾者模式、外觀模式、享元模式、代理模式;
3、行為型模式:觀察者模式、模板方法、命令模式、狀態(tài)模式、職責(zé)鏈模式、解釋器模式、中介者模式、訪問者模式、策略模式、備忘錄模式、迭代器模式。
4、MVC模式:集觀察者、組合、策略為一體,是多種模式的綜合應(yīng)用,算是一種架構(gòu)模式。
?
下面按照【概念】+【原則】+【場(chǎng)景】+【優(yōu)點(diǎn)】+【缺點(diǎn)】+【應(yīng)用】分別簡(jiǎn)述一下24種設(shè)計(jì)模式:
?
1、抽象工廠模式(Abstract Factory)
提供一個(gè)創(chuàng)建一系列相關(guān)或互相依賴對(duì)象的接口,而無需指定它們具體的類。
原則:
?? ?LSP 里氏替換原則
場(chǎng)景:
?? ?創(chuàng)建不同的產(chǎn)品對(duì)象,客戶端應(yīng)使用不同的具體工廠。
優(yōu)點(diǎn):
?? ?1、改變具體工廠即可使用不同的產(chǎn)品配置,使改變一個(gè)應(yīng)用的具體工廠變得很容易。
?? ?2、讓具體的創(chuàng)建實(shí)例過程與客戶端分離,客戶端通過抽象接口操作實(shí)例,產(chǎn)品的具體類名也被具體工廠的實(shí)現(xiàn)分離。
缺點(diǎn):
?? ?如果要新增方法,改動(dòng)極大。
應(yīng)用:
?? ?1、jdk中連接數(shù)據(jù)庫的代碼是典型的抽象工廠模式,每一種數(shù)據(jù)庫只需提供一個(gè)統(tǒng)一的接口:Driver(工廠類),并實(shí)現(xiàn)其中的方法即可。不管是jdbc還是odbc都能夠通過擴(kuò)展產(chǎn)品線來達(dá)到連接自身數(shù)據(jù)庫的方法。
?? ?2、java.util.Collection 接口中定義了一個(gè)抽象的 iterator() 方法,該方法就是一個(gè)工廠方法。對(duì)于 iterator() 方法來說 Collection 就是一個(gè)抽象工廠。
?
2、建造者模式(Builder)【又名,生成器模式】
將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
原則:
?? ?依賴倒轉(zhuǎn)原則
場(chǎng)景:
?? ?如果需要將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。建造者模式是當(dāng)創(chuàng)建復(fù)雜對(duì)象的算法應(yīng)該獨(dú)立于該對(duì)象的組成部分以及它們的裝配方式時(shí)適用的模式。
優(yōu)點(diǎn):
?? ?使得建造代碼與表示代碼分離。
缺點(diǎn):
?? ?1、增加代碼量;
?? ?2、Builder只是一個(gè)替代構(gòu)造器的選擇,不能直接用于降低非構(gòu)造函數(shù)方法的參數(shù)數(shù)量。
應(yīng)用:
?? ?StringBuilder和StringBuffer的append()方法
?
3、工廠方法模式(Factory Method)
定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類,工廠方法使一個(gè)類的實(shí)例化延遲到其子類。
原則:
?? ?開放封閉原則
場(chǎng)景:
?? ?不改變工廠和產(chǎn)品體系,只是要擴(kuò)展產(chǎn)品(變化)。
優(yōu)點(diǎn):
?? ?是簡(jiǎn)單工廠模式的進(jìn)一步抽象和推廣,既保持了簡(jiǎn)單工廠模式的優(yōu)點(diǎn)(工廠類中包含了必要的邏輯判斷,根據(jù)客戶端的選擇條件動(dòng)態(tài)實(shí)例化相關(guān)的類。對(duì)于客戶端來說,去除了與具體產(chǎn)品的依賴),而且克服了簡(jiǎn)單工廠的缺點(diǎn)(違背了開放封閉原則)。
缺點(diǎn):
?? ?每增加一個(gè)產(chǎn)品,就需要增加一個(gè)產(chǎn)品工廠的類,增加了額外的開發(fā)。(用反射可以解決)。
應(yīng)用:
?? ?1、Collection中的iterator方法;
?? ?2、java.lang.Proxy#newProxyInstance()
?? ?3、java.lang.Object#toString()
?? ?4、java.lang.Class#newInstance()
?? ?5、java.lang.reflect.Array#newInstance()
?? ?6、java.lang.reflect.Constructor#newInstance()
?? ?7、java.lang.Boolean#valueOf(String)
?? ?8、java.lang.Class#forName()
?
4、原型模式(prototype):【又名,生成器模式】
用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過拷貝這些原型創(chuàng)建新的對(duì)象。
原則:
場(chǎng)景:
?? ?在初始化信息不發(fā)生變化的情況,用克隆進(jìn)行拷貝。
優(yōu)點(diǎn):
?? ?隱藏了對(duì)象創(chuàng)建的細(xì)節(jié),大大提升了性能。不用重新初始化對(duì)象,而是動(dòng)態(tài)的獲得對(duì)象運(yùn)行時(shí)的狀態(tài)。
缺點(diǎn):
?? ?深復(fù)制 or 淺復(fù)制 。
應(yīng)用:
?? ?JDK中的Date類。
?
5、單例模式(Singleton)
保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。
原則:
?? ?封裝
場(chǎng)景:
?? ?通常,我們可以讓一個(gè)全局變量使得一個(gè)對(duì)象被訪問,但它不能防止你實(shí)例化多個(gè)對(duì)象,一個(gè)最好的辦法就是,讓類自身負(fù)責(zé)保存它的唯一實(shí)例。這個(gè)類可以保證沒有其他實(shí)例可以被創(chuàng)建,而且它可以提供一個(gè)訪問該實(shí)例的方法。
優(yōu)點(diǎn):
?? ?對(duì)唯一實(shí)例的受控訪問。
缺點(diǎn):
?? ?餓漢式/懶漢式??多線程同時(shí)訪問時(shí)可能造成多個(gè)實(shí)例。
應(yīng)用:
?? ?1、java.lang.Runtime; GUI中也有一些(java.awt.Toolkit#getDefaultToolkit()?java.awt.Desktop#getDesktop())
?
6、適配器模式(Adapter)
將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。在GoF的設(shè)計(jì)模式中,適配器有兩種類型,類適配器模式和對(duì)象適配器模式。
?? ?1、類適配器模式:通過多重繼承對(duì)一個(gè)接口與另一個(gè)接口進(jìn)行匹配,而C#,Java等語言都不支持多重繼承,也就是一個(gè)類只有一個(gè)父類。
?? ?2、Java一般都指的是 對(duì)象適配器模式
場(chǎng)景:
?? ?適配器是為了復(fù)用一些現(xiàn)有的類。系統(tǒng)的數(shù)據(jù)和行為都正確,但是接口不符,這時(shí)采用適配器模式,使原有對(duì)象和新接口匹配。
優(yōu)點(diǎn):
?? ?能夠復(fù)用現(xiàn)存的類,客戶端統(tǒng)一調(diào)用同一接口,更簡(jiǎn)單、直接、緊湊。
缺點(diǎn):
?? ?適配器模式有點(diǎn)兒“亡羊補(bǔ)牢”的感覺,設(shè)計(jì)階段要避免使用。
應(yīng)用:
?? ?在Java jdk中,適配器模式使用場(chǎng)景很多,如
?? ?集合包中Java.util.Arrays#asList()、
?? ?IO包中java.io.InputStreamReader(InputStream)、java.io.OutputStreamWriter(OutputStream) 等
?
7、橋接模式(Bridge)
將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立的變化。
原則:
?? ?合成/聚合復(fù)用原則
場(chǎng)景:
?? ?實(shí)現(xiàn)系統(tǒng)可能有多角度分類,每一種分類都有可能變化,那么就把這種多角度分離出來讓它們獨(dú)立變化,減少它們之間的耦合。
優(yōu)點(diǎn):
?? ?減少各部分的耦合。 分離抽象和實(shí)現(xiàn)部分,更好的擴(kuò)展性,可動(dòng)態(tài)地切換實(shí)現(xiàn)、可減少子類的個(gè)數(shù)。
缺點(diǎn):
?? ?1、橋接模式的引入會(huì)增加系統(tǒng)的理解與設(shè)計(jì)難度,由于聚合關(guān)聯(lián)關(guān)系建立在抽象層,要求開發(fā)者針對(duì)抽象進(jìn)行設(shè)計(jì)與編程。?
?? ?2、橋接模式要求正確識(shí)別出系統(tǒng)中兩個(gè)獨(dú)立變化的維度,因此其使用范圍具有一定的局限性
應(yīng)用:
?? ?Collections類中的sort()方法;AWT;JDBC數(shù)據(jù)庫訪問接口API;
?
8、組合模式(Composite)
將對(duì)象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。
場(chǎng)景:
?? ?需求中體現(xiàn)部分與整體層次結(jié)構(gòu)時(shí),以及希望用戶可以忽略組合對(duì)象與單個(gè)對(duì)象的不同,統(tǒng)一使用組合結(jié)構(gòu)中的所有對(duì)象時(shí),就應(yīng)該考慮使用組合模式了。
優(yōu)點(diǎn):
?? ?組合模式讓客戶可以一致的使用組合結(jié)構(gòu)和單個(gè)對(duì)象。
缺點(diǎn):
?? ?使設(shè)計(jì)變得更加抽象,對(duì)象的業(yè)務(wù)規(guī)則如果很復(fù)雜,則實(shí)現(xiàn)組合模式具有很大挑戰(zhàn)性,而且不是所有的方法都與葉子對(duì)象子類都有關(guān)聯(lián)。
應(yīng)用:
?? ?JDK中AWT包和Swing包的設(shè)計(jì)是基于組合模式,在這些界面包中為用戶提供了大量的容器構(gòu)件(如Container)和成員構(gòu)件(如Checkbox、Button和TextComponent等),他們都是繼承、關(guān)聯(lián)自抽象組件類Component。
?
9、裝飾模式(Decorator)
動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé),就增加功能來說,裝飾模式比生成子類更靈活。
場(chǎng)景:
?? ?裝飾模式是為了已有功能動(dòng)態(tài)地添加更多功能的一種方式,當(dāng)系統(tǒng)需要新功能的時(shí)候,是向舊類中添加新的代碼,這些新的代碼通常裝飾了原有類的核心職責(zé)或主要行為。裝飾著模式把每個(gè)要裝飾的功能放在單獨(dú)的類中,并讓這個(gè)類包裝它所要裝飾的對(duì)象,當(dāng)需要執(zhí)行特殊行為時(shí),客戶代碼就可以在運(yùn)行時(shí)根據(jù)需要有選擇的、按順序地使用裝飾功能包裝對(duì)象。
優(yōu)點(diǎn):
?? ?把類中的裝飾功能從類中搬移出去,簡(jiǎn)化原有的類。有效的把類的核心職責(zé)和裝飾功能區(qū)分開,去除相關(guān)類中重復(fù)的裝飾邏輯。
缺點(diǎn):
?? ?利用裝飾器模式,常常造成設(shè)計(jì)中有大量的小類,數(shù)量實(shí)在太多,可能會(huì)造成使用此API程序員的困擾。
應(yīng)用:
?? ?Java I/O使用裝飾模式設(shè)計(jì),JDK中還有很多類是使用裝飾模式設(shè)計(jì)的,如:Reader類、Writer類、OutputStream類等。
?
10、外觀模式(facade)
為子系統(tǒng)中的一組接口提供一個(gè)一致的界面,此模式定義了一個(gè)高層接口,這個(gè)接口使得這一子系統(tǒng)更加容易使用。
原則:
?? ?完美的體現(xiàn)了依賴倒轉(zhuǎn)原則和迪米特法則。
場(chǎng)景:
?? ?1、設(shè)計(jì)階段:需有意識(shí)的將不同的兩個(gè)層分離。
?? ?2、開發(fā)階段:增加外觀fa?ade提供一個(gè)簡(jiǎn)單的接口,應(yīng)對(duì)子類的重演和演化。
?? ?3、維護(hù)期間:使用fa?ade類,為遺留代碼提供清晰簡(jiǎn)單的接口,讓新系統(tǒng)與fa?ade交互,fa?ade與遺留代碼交互所有復(fù)雜的工作。
優(yōu)點(diǎn):
?? ?1、客戶對(duì)子系統(tǒng)的使用變得簡(jiǎn)單了,減少了與子系統(tǒng)的關(guān)聯(lián)對(duì)象,實(shí)現(xiàn)了子系統(tǒng)與客戶之間的松耦合關(guān)系。?
?? ?2、只是提供了一個(gè)訪問子系統(tǒng)的統(tǒng)一入口,并不影響用戶直接使用子系統(tǒng)類?
?? ?3、降低了大型軟件系統(tǒng)中的編譯依賴性,并簡(jiǎn)化了系統(tǒng)在不同平臺(tái)之間的移植過程。
缺點(diǎn):
?? ?1、不能很好地限制客戶使用子系統(tǒng)類,如果對(duì)客戶訪問子系統(tǒng)類做太多的限制則減少了可變性和靈活性???
?? ?2、在不引入抽象外觀類的情況下,增加新的子系統(tǒng)可能需要修改外觀類或客戶端的源代碼,違背了“開閉原則”。
?
11、享元模式(Flyweight)
運(yùn)用共享技術(shù)有效的支持大量細(xì)粒度的對(duì)象。
場(chǎng)景:
?? ?如果一個(gè)應(yīng)用程序使用了大量的對(duì)象,而大量的這些對(duì)象造成了很大存儲(chǔ)開銷時(shí)就應(yīng)該考慮使用享元模式;還有就是對(duì)象大多數(shù)狀態(tài)都可為外部狀態(tài),如果刪除對(duì)象的外部狀態(tài),那么可以用相對(duì)較少的共享對(duì)象取代很多組對(duì)象,此時(shí)可以考慮使用享元模式。
優(yōu)點(diǎn):
?? ?享元模式可以避免大量非常相似類的開銷。程序中,大量細(xì)粒度的類實(shí)例來表示數(shù)據(jù),如果它們除了幾個(gè)參數(shù)外基本相同,那么把它們轉(zhuǎn)移到類實(shí)例的外面,在方法調(diào)用時(shí)將它們傳遞進(jìn)來,就可以通過共享大幅度減少單個(gè)實(shí)例的數(shù)目。
缺點(diǎn):
?? ?1、由于享元模式需要區(qū)分外部狀態(tài)和內(nèi)部狀態(tài),使得應(yīng)用程序在某種程度上來說更加復(fù)雜化了。
?? ?2、為了使對(duì)象可以共享,享元模式需要將享元對(duì)象的狀態(tài)外部化,而讀取外部狀態(tài)使得運(yùn)行時(shí)間變長。
應(yīng)用:
?? ?String 類。
?
12、代理模式(proxy)
為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。
原則:
?? ?代理模式就是在訪問對(duì)象時(shí)引入一定程度的間接性。(迪米特法則?)
場(chǎng)景:
?? ?1、遠(yuǎn)程代理:為一個(gè)對(duì)象在不同的地址空間提供局部代表,這樣可以隱藏一個(gè)對(duì)象存在于不同地址空間的事實(shí)。【W(wǎng)ebService,客戶端可以調(diào)用代理解決遠(yuǎn)程訪問問題】
?? ?2、虛擬代理:根據(jù)需要?jiǎng)?chuàng)建開銷很大的對(duì)象,通過它來存放實(shí)例化需要很長時(shí)間地真實(shí)對(duì)象。【比如Html網(wǎng)頁的圖片,代理存儲(chǔ)的是真實(shí)圖片的路徑和尺寸】
?? ?3、安全代理:用來控制真實(shí)對(duì)象的訪問權(quán)限。
?? ?4、智能指引:當(dāng)調(diào)用真實(shí)的對(duì)象時(shí),代理處理另一些事。【如計(jì)算機(jī)真實(shí)對(duì)象的引用次數(shù),代理在訪問一個(gè)對(duì)象的時(shí)候回附加一些內(nèi)務(wù)處理,檢查對(duì)象是否被鎖定、是否該釋放、是否該裝入內(nèi)存等等】
優(yōu)點(diǎn):
?? ?1、代理模式能將代理對(duì)象與真正被調(diào)用的對(duì)象分離,在一定程度上降低了系統(tǒng)的耦合度。
?? ?2、代理模式在客戶端和目標(biāo)對(duì)象之間起到一個(gè)中介作用,這樣可以起到保護(hù)目標(biāo)對(duì)象的作用。代理對(duì)象也可以對(duì)目標(biāo)對(duì)象調(diào)用之前進(jìn)行其他操作。
缺點(diǎn):
?? ?1、在客戶端和目標(biāo)對(duì)象增加一個(gè)代理對(duì)象,會(huì)造成請(qǐng)求處理速度變慢。
?? ?2、增加了系統(tǒng)的復(fù)雜度。
應(yīng)用:
?? ?java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動(dòng)態(tài)代理類的能力。
?
13、觀察者模式(Publish/Subscribe) 【又名 發(fā)布-訂閱模式】
定義了一種一對(duì)多的依賴關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽某一個(gè)主題對(duì)象。這個(gè)主題對(duì)象在狀態(tài)發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,讓它們能夠自動(dòng)更新自己。
場(chǎng)景:
?? ?將一個(gè)系統(tǒng)分割成一系列互相協(xié)作的類,有一個(gè)缺點(diǎn):需要維護(hù)相關(guān)對(duì)象間的一致性。緊密的耦合會(huì)給維護(hù)和擴(kuò)展帶來不便。觀察者模式就是為了解耦而誕生的,讓原本一個(gè)對(duì)象依賴另一個(gè)對(duì)象的關(guān)系,變成了兩方都依賴于抽象,而不再依賴于具體,從而使得各自的變化都不會(huì)影響另一邊的變化。
優(yōu)點(diǎn):
?? ?解耦。
缺點(diǎn):
?? ?如果在被觀察者之間有循環(huán)依賴的話,被觀察者會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,導(dǎo)致系統(tǒng)崩潰。在使用觀察者模式是要特別注意這一點(diǎn)。
應(yīng)用:
?? ?java.util.Observer, java類庫實(shí)現(xiàn)觀察著(Observer)模式的類和接口。
?
14、模板方法模式(Template Method)
定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類中。
原則:
?? ?代碼復(fù)用平臺(tái)。
場(chǎng)景:
?? ?遇到由一系列步驟構(gòu)成的過程需要執(zhí)行,這個(gè)過程從高層次上看是相同的,但是有些步驟的實(shí)現(xiàn)可能不同,這個(gè)時(shí)候就需要考慮用模板方法模式了。
優(yōu)點(diǎn):
?? ?模板方法模式是通過把不變行為搬移到超類,去除子類中重復(fù)代碼來實(shí)現(xiàn)它的優(yōu)勢(shì),提供了一個(gè)代碼復(fù)用平臺(tái),幫助子類擺脫重復(fù)的不變行為的糾纏。
缺點(diǎn):
?? ?如果父類中可變的基本方法太多,將會(huì)導(dǎo)致類的個(gè)數(shù)增加,系統(tǒng)更加龐大。
應(yīng)用:
?? ?AbstractClass抽象類里面的TemplateMethod()就是模板方法。
?
15、命令模式(command)
將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,從而使你可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化;對(duì)請(qǐng)求排隊(duì)或記錄請(qǐng)求日志,以及支持可撤銷的操作。
原則:
?? ?敏捷開發(fā)原則
場(chǎng)景:
?? ?對(duì)請(qǐng)求排隊(duì)或記錄請(qǐng)求日志,以及支持可撤銷的操作等行為。
優(yōu)點(diǎn):
?? ?1、命令模式把請(qǐng)求一個(gè)操作的對(duì)象與知道怎么執(zhí)行一個(gè)操作的對(duì)象分割開。
?? ?2、它能較容易的設(shè)計(jì)一個(gè)命令隊(duì)列。
?? ?3、在需要的情況下,可以較容易的將命令記入日志。
?? ?4、允許接收請(qǐng)求的一方?jīng)Q定是否要否決請(qǐng)求。
?? ?5、可以容易的實(shí)現(xiàn)對(duì)請(qǐng)求的撤銷和重做。
?? ?6、由于加進(jìn)新的具體命令類不影響其他類,因此增加新的具體命令類很容易。
缺點(diǎn):
?? ?會(huì)增加系統(tǒng)的復(fù)雜性,這里的復(fù)雜性應(yīng)該主要指的是類的數(shù)量。
應(yīng)用:
?? ?1、java.util.Timer類中scheduleXXX()方法
?? ?2、java Concurrency Executor execute()方法
?? ?3、java.lang.reflect.Methodinvoke()方法
?
16、狀態(tài)模式(state)
當(dāng)一個(gè)對(duì)象的內(nèi)在狀態(tài)改變時(shí),允許改變其行為,這個(gè)對(duì)象看起來像是改變了其類。
原則:
?? ?單一職責(zé)原則
場(chǎng)景:
?? ?當(dāng)一個(gè)對(duì)象的行為取決于它的狀態(tài),并且它必須在運(yùn)行時(shí)刻根據(jù)狀態(tài)改變它的行為時(shí),可以考慮使用狀態(tài)模式了。
優(yōu)點(diǎn):
?? ?狀態(tài)模式主要解決的是當(dāng)控制一個(gè)對(duì)象狀態(tài)轉(zhuǎn)換的條件表達(dá)式過于復(fù)雜的情況。把狀態(tài)的判斷邏輯轉(zhuǎn)移到表示不同狀態(tài)的一系列類當(dāng)中,可以把復(fù)雜的判斷邏輯簡(jiǎn)化。【消除龐大的條件分支語句】。
缺點(diǎn):
?? ?違背開放-封閉原則
應(yīng)用:
?? ?1、java.util.Iterator
?? ?2、javax.faces.lifecycle.LifeCycle#execute()
?
17、職責(zé)鏈模式(chain of responsibility)
使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系。將這個(gè)對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有一個(gè)對(duì)象處理它為止。
場(chǎng)景:
?? ?當(dāng)客戶提交一個(gè)請(qǐng)求時(shí),請(qǐng)求是沿鏈傳遞直至有一個(gè)對(duì)象負(fù)責(zé)處理它。
優(yōu)點(diǎn):
?? ?使得接收者和發(fā)送者都沒有對(duì)方的明確信息,且鏈中對(duì)象自己也不知道鏈結(jié)構(gòu),結(jié)果是職責(zé)鏈可以簡(jiǎn)化對(duì)象的相互連接,它們只需要保持一個(gè)指向其后繼者的引用,而不需要保持它所有的候選接收者的引用。開發(fā)者可以隨時(shí)的增加或者修改處理一個(gè)請(qǐng)求的結(jié)構(gòu),增強(qiáng)了給對(duì)象指派職責(zé)的靈活性。
缺點(diǎn):
?? ?一個(gè)請(qǐng)求極有可能到了鏈的末端都得不到處理,或者因?yàn)闆]有正確配置而得不到處理。
?
18、解釋器模式(interpreter)
給定一個(gè)語言,定義它的文法的一種表示,并定義一個(gè)解釋器,這個(gè)解釋器使用該表示來解釋語言中的句子。
原則:
?? ?依賴倒轉(zhuǎn)原則
場(chǎng)景:
?? ?如果一種特定類型問題發(fā)生的頻率足夠高,那么可能就值得將該問題的各個(gè)實(shí)例表述為一個(gè)簡(jiǎn)單語句中的句子。這樣就可以構(gòu)建一個(gè)解釋器,該解釋器通過解釋這些句子來解決該問題。當(dāng)一個(gè)語言需要執(zhí)行,并且你可將該語言中的句子表示為一個(gè)抽象語法樹時(shí),可以用解釋器模式。
優(yōu)點(diǎn):
?? ?解釋器很容易改變和擴(kuò)展文法,因?yàn)樵撃J绞褂妙悂肀硎疚姆ㄒ?guī)則,可以使用繼承來改變或擴(kuò)展文法,也比較容易實(shí)現(xiàn)文法。因?yàn)槎x抽象語法樹中各個(gè)節(jié)點(diǎn)的類的實(shí)現(xiàn)大體類似,這些類都易于直接編寫。
缺點(diǎn):
?? ?解釋器模式為文法中的每一條規(guī)則至少定義了一個(gè)類,因此包含許多規(guī)則的文法可能難以管理和維護(hù),建議當(dāng)文法非常復(fù)雜時(shí),使用其他技術(shù)(語法分析程序、編譯器生成器)。
應(yīng)用:
?? ?1、java.util.Pattern
?? ?2、java.text.Normalizer
?? ?3、java.text.Format
?? ?4、javax.el.ELResolver
?
19、中介者模式(mediator)
用一個(gè)中介對(duì)象來封裝一系列的對(duì)象交互。中介者使各對(duì)象不需要顯示的相互引用,從而使其耦合松散,而且可以獨(dú)立的改變它們之間的交互。
場(chǎng)景:
?? ?一般應(yīng)用于一組對(duì)象以定義良好但是復(fù)雜的方式進(jìn)行通信的場(chǎng)合,以及想定制一個(gè)分布在多個(gè)類的行為,而又不想生成太多子類的場(chǎng)合。【例如,Form窗體,或者aspx頁面】。
優(yōu)點(diǎn):
?? ?1、抽象中介者類(Mediator)減少了抽象同事類(colleague)之間的耦合,是的可以獨(dú)立的改變和復(fù)用各個(gè)類。
?? ?2、由于把對(duì)象如何協(xié)作進(jìn)行了抽象,將中介作為一個(gè)獨(dú)立的概念并將其封裝在一個(gè)對(duì)象中,這樣關(guān)注的對(duì)象就從對(duì)象各自本身的行為轉(zhuǎn)移到它們之間的交互上來,也就是站在一個(gè)更宏觀的角度去看待系統(tǒng)。
缺點(diǎn):
?? ?控制集中化導(dǎo)致了中介者的復(fù)雜化。
應(yīng)用:
?? ?1、java.util.Timer
?? ?2、java.util.concurrent.Executor#execute()
?? ?3、java.util.concurrent.ExecutorService#submit()
?? ?4、java.lang.reflect.Method#invoke()
?
20、訪問者模式 (Vistor) 生成器模式
(GoF中最復(fù)雜的一個(gè)模式)表示一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作,它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。
場(chǎng)景:
?? ?訪問者模式適合有穩(wěn)定的數(shù)據(jù)結(jié)構(gòu)、又有易于變化的算法】訪問者模式適用于數(shù)據(jù)結(jié)構(gòu)相對(duì)穩(wěn)定的系統(tǒng),它把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作之間的耦合解脫開,是的操作集合可以相對(duì)自由的演化。訪問者模式的目的是要把處理從數(shù)據(jù)結(jié)構(gòu)中分離出來。
優(yōu)點(diǎn):
?? ?增加新的操作很容易。新的操作就是新的訪問者。
缺點(diǎn):
?? ?很難增加新的數(shù)據(jù)結(jié)構(gòu)。
應(yīng)用:
?? ?1、javax.lang.model.element.AnnotationValue和AnnotationValueVisitor
?? ?2、javax.lang.model.element.Element和ElementVisitor
?? ?3、javax.lang.model.type.TypeMirror和TypeVisitor
?
21、策略模式(strategy)
它定義了算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化不會(huì)影響到使用算法的用戶。
場(chǎng)景:
?? ?策略模式不僅可以用來封裝算法,幾乎可以封裝縫合類型的規(guī)則,不同的業(yè)務(wù)邏輯都可以考慮用策略模式處理變化。
優(yōu)點(diǎn):
?? ?策略模式的策略類為上下文定義了一系列可供重用的算法或行為,繼承有助于析取出這些算法中的公共功能。另外,策略模式簡(jiǎn)化了單元測(cè)試,因?yàn)槊恳粋€(gè)算法都有自己的類,可以通過自己的接口單獨(dú)測(cè)試。當(dāng)不同的行為堆砌在一個(gè)類中,很難避免使用switch語句。但是將這些行為封裝在一個(gè)一個(gè)獨(dú)立的策略類中,可以在使用這些行為的類中消除條件語句
缺點(diǎn):
?? ?基本的策略模式,選擇權(quán)在客戶端,具體實(shí)現(xiàn)轉(zhuǎn)給策略模式的上下文對(duì)象。這并不好。使用策略模式和工廠類結(jié)合,可以減輕客戶端的職責(zé)。但是還是不夠完美,使用反射才能真正快樂。
應(yīng)用:
?? ?1、java.util.Comparator#compare()
?? ?2、javax.servlet.http.HttpServlet
?? ?3、javax.servlet.Filter#doFilter()
?
22、備忘錄模式(Memento)
在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),這樣以后就可將該對(duì)象恢復(fù)到原先保存的狀態(tài)。
場(chǎng)景:
?? ?Memento封裝要保存的細(xì)節(jié),適合功能負(fù)責(zé)但需要維護(hù)或記錄屬性歷史的類,或者是需要保存的屬性只是眾多屬性中的一個(gè)小部分。
優(yōu)點(diǎn):
?? ?使用備忘錄模式可以把復(fù)雜的發(fā)起人內(nèi)部信息對(duì)其他的對(duì)象屏蔽起來,從而可以恰當(dāng)?shù)乇3址庋b的邊界。
缺點(diǎn):
?? ?如果發(fā)起人角色的狀態(tài)需要完整地存儲(chǔ)到備忘錄對(duì)象中,那么在資源消耗上面?zhèn)渫泴?duì)象會(huì)很昂貴。
應(yīng)用:
?? ?1、java.util.Date
?? ?2、java.io.Serializable
?
23、迭代器模式(Iterator)
提供一種方法順序訪問一個(gè)聚合對(duì)象中各個(gè)元素,而又不暴露該對(duì)象的內(nèi)部表示。
場(chǎng)景:
?? ?當(dāng)需要對(duì)聚集有多種方式遍歷時(shí),可以考慮使用迭代器。
優(yōu)點(diǎn):
?? ?迭代器模式就是分離了集合對(duì)象的遍歷行為,抽象出一個(gè)迭代器來負(fù)責(zé),這樣既可以做到不暴露集合的內(nèi)部結(jié)構(gòu),又可以讓外部代碼透明的訪問集合內(nèi)部的數(shù)據(jù)。
缺點(diǎn):
?? ?由于迭代器模式將存儲(chǔ)數(shù)據(jù)和遍歷數(shù)據(jù)的職責(zé)分離,增加新的聚合類需要對(duì)應(yīng)增加新的迭代器類,類的個(gè)數(shù)成對(duì)增加,這在一定程度上增加了系統(tǒng)的復(fù)雜性。
應(yīng)用:
?? ?collection容器使用了迭代器模式
?
?
總結(jié)
以上是生活随笔為你收集整理的GOF设计模式(概念、原则、场景、优点、缺点、应用)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: awr报告分析 mysql_AWR报告的
- 下一篇: 设计模式-GRASP V.S GoF