什么是 GOF(四人幫阀趴,全拼 Gang of Four)碌尔?
在 1994 年,由 Erich Gamma朋魔、Richard Helm岖研、Ralph Johnson 和 John Vlissides 四人合著出版了一本名為?Design Patterns - Elements of Reusable Object-Oriented Software(中文譯名:設(shè)計(jì)模式 - 可復(fù)用的面向?qū)ο筌浖兀?/b>?的書,該書首次提到了軟件開發(fā)中設(shè)計(jì)模式的概念铺厨。
四位作者合稱?GOF(四人幫缎玫,全拼 Gang of Four)。他們所提出的設(shè)計(jì)模式主要是基于以下的面向?qū)ο笤O(shè)計(jì)原則解滓。
對接口編程而不是對實(shí)現(xiàn)編程。
優(yōu)先使用對象組合而不是繼承筝家。
適配器模式Adapter Pattern
適配器模式(Adapter Pattern)是作為兩個(gè)不兼容的接口之間的橋梁洼裤。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它結(jié)合了兩個(gè)獨(dú)立接口的功能溪王。
這種模式涉及到一個(gè)單一的類腮鞍,該類負(fù)責(zé)加入獨(dú)立的或不兼容的接口功能。舉個(gè)真實(shí)的例子莹菱,讀卡器是作為內(nèi)存卡和筆記本之間的適配器移国。您將內(nèi)存卡插入讀卡器,再將讀卡器插入筆記本道伟,這樣就可以通過筆記本來讀取內(nèi)存卡迹缀。
我們通過下面的實(shí)例來演示適配器模式的使用。其中蜜徽,音頻播放器設(shè)備只能播放 mp3 文件祝懂,通過使用一個(gè)更高級的音頻播放器來播放 vlc 和 mp4 文件。
意圖:將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口拘鞋。適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作砚蓬。
主要解決:主要解決在軟件系統(tǒng)中,常常要將一些"現(xiàn)存的對象"放到新的環(huán)境中盆色,而新環(huán)境要求的接口是現(xiàn)對象不能滿足的灰蛙。
何時(shí)使用:?1、系統(tǒng)需要使用現(xiàn)有的類隔躲,而此類的接口不符合系統(tǒng)的需要摩梧。 2、想要建立一個(gè)可以重復(fù)使用的類蹭越,用于與一些彼此之間沒有太大關(guān)聯(lián)的一些類障本,包括一些可能在將來引進(jìn)的類一起工作,這些源類不一定有一致的接口。 3驾霜、通過接口轉(zhuǎn)換案训,將一個(gè)類插入另一個(gè)類系中。(比如老虎和飛禽粪糙,現(xiàn)在多了一個(gè)飛虎强霎,在不增加實(shí)體的需求下,增加一個(gè)適配器蓉冈,在里面包容一個(gè)虎對象城舞,實(shí)現(xiàn)飛的接口。)
如何解決:繼承或依賴(推薦)寞酿。
關(guān)鍵代碼:適配器繼承或依賴已有的對象家夺,實(shí)現(xiàn)想要的目標(biāo)接口。
應(yīng)用實(shí)例:?1伐弹、美國電器 110V拉馋,中國 220V,就要有一個(gè)適配器將 110V 轉(zhuǎn)化為 220V惨好。 2煌茴、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口日川,想要使用 1.2 的 JDK蔓腐,則要將以前系統(tǒng)的 Enumeration 接口轉(zhuǎn)化為 Iterator 接口,這時(shí)就需要適配器模式龄句。 3回论、在 LINUX 上運(yùn)行 WINDOWS 程序。 4撒璧、JAVA 中的 jdbc透葛。
優(yōu)點(diǎn):?1、可以讓任何兩個(gè)沒有關(guān)聯(lián)的類一起運(yùn)行卿樱。 2僚害、提高了類的復(fù)用。 3繁调、增加了類的透明度萨蚕。 4、靈活性好蹄胰。
缺點(diǎn):?1岳遥、過多地使用適配器,會讓系統(tǒng)非常零亂浩蓉,不易整體進(jìn)行把握派继。比如捻艳,明明看到調(diào)用的是 A 接口,其實(shí)內(nèi)部被適配成了 B 接口的實(shí)現(xiàn)绅络,一個(gè)系統(tǒng)如果太多出現(xiàn)這種情況,無異于一場災(zāi)難恩急。因此如果不是很有必要,可以不使用適配器衷恭,而是直接對系統(tǒng)進(jìn)行重構(gòu)。 2.由于 JAVA 至多繼承一個(gè)類霍掺,所以至多只能適配一個(gè)適配者類匾荆,而且目標(biāo)類必須是抽象類。
使用場景:有動機(jī)地修改一個(gè)正常運(yùn)行的系統(tǒng)的接口杆烁,這時(shí)應(yīng)該考慮使用適配器模式。
注意事項(xiàng):適配器不是在詳細(xì)設(shè)計(jì)時(shí)添加的简卧,而是解決正在服役的項(xiàng)目的問題兔魂。
工廠模式Factory Pattern
工廠模式(Factory Pattern)是 Java 中最常用的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式举娩,它提供了一種創(chuàng)建對象的最佳方式析校。
在工廠模式中,我們在創(chuàng)建對象時(shí)不會對客戶端暴露創(chuàng)建邏輯铜涉,并且是通過使用一個(gè)共同的接口來指向新創(chuàng)建的對象智玻。
介紹
意圖:定義一個(gè)創(chuàng)建對象的接口,讓其子類自己決定實(shí)例化哪一個(gè)工廠類芙代,工廠模式使其創(chuàng)建過程延遲到子類進(jìn)行吊奢。
主要解決:主要解決接口選擇的問題。
何時(shí)使用:我們明確地計(jì)劃不同條件下創(chuàng)建不同實(shí)例時(shí)纹烹。
如何解決:讓其子類實(shí)現(xiàn)工廠接口页滚,返回的也是一個(gè)抽象的產(chǎn)品。
關(guān)鍵代碼:創(chuàng)建過程在其子類執(zhí)行铺呵。
應(yīng)用實(shí)例:?1裹驰、您需要一輛汽車,可以直接從工廠里面提貨片挂,而不用去管這輛汽車是怎么做出來的幻林,以及這個(gè)汽車?yán)锩娴木唧w實(shí)現(xiàn)贞盯。 2、Hibernate 換數(shù)據(jù)庫只需換方言和驅(qū)動就可以沪饺。
優(yōu)點(diǎn):?1躏敢、一個(gè)調(diào)用者想創(chuàng)建一個(gè)對象,只要知道其名稱就可以了随闽。 2父丰、擴(kuò)展性高掘宪,如果想增加一個(gè)產(chǎn)品魏滚,只要擴(kuò)展一個(gè)工廠類就可以鼠次。 3腥寇、屏蔽產(chǎn)品的具體實(shí)現(xiàn)赦役,調(diào)用者只關(guān)心產(chǎn)品的接口掂摔。
缺點(diǎn):每次增加一個(gè)產(chǎn)品時(shí)乙漓,都需要增加一個(gè)具體類和對象實(shí)現(xiàn)工廠叭披,使得系統(tǒng)中類的個(gè)數(shù)成倍增加趋观,在一定程度上增加了系統(tǒng)的復(fù)雜度皱坛,同時(shí)也增加了系統(tǒng)具體類的依賴。這并不是什么好事掐场。
使用場景:?1熊户、日志記錄器:記錄可能記錄到本地硬盤嚷堡、系統(tǒng)事件蝌戒、遠(yuǎn)程服務(wù)器等北苟,用戶可以選擇記錄日志到什么地方友鼻。 2彩扔、數(shù)據(jù)庫訪問虫碉,當(dāng)用戶不知道最后系統(tǒng)采用哪一類數(shù)據(jù)庫蔗衡,以及數(shù)據(jù)庫可能有變化時(shí)绞惦。 3济蝉、設(shè)計(jì)一個(gè)連接服務(wù)器的框架王滤,需要三個(gè)協(xié)議雁乡,"POP3"踱稍、"IMAP"珠月、"HTTP"啤挎,可以把這三個(gè)作為產(chǎn)品類庆聘,共同實(shí)現(xiàn)一個(gè)接口掏觉。
注意事項(xiàng):作為一種創(chuàng)建類模式澳腹,在任何需要生成復(fù)雜對象的地方酱塔,都可以使用工廠方法模式羊娃。有一點(diǎn)需要注意的地方就是復(fù)雜對象適合使用工廠模式蕊玷,而簡單對象垃帅,特別是只需要通過 new 就可以完成創(chuàng)建的對象贸诚,無需使用工廠模式酱固。如果使用工廠模式运悲,就需要引入一個(gè)工廠類扇苞,會增加系統(tǒng)的復(fù)雜度。
抽象工廠模式
抽象工廠模式(Abstract Factory Pattern)是圍繞一個(gè)超級工廠創(chuàng)建其他工廠程拭。該超級工廠又稱為其他工廠的工廠恃鞋。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式恤浪,它提供了一種創(chuàng)建對象的最佳方式水由。
在抽象工廠模式中砂客,接口是負(fù)責(zé)創(chuàng)建一個(gè)相關(guān)對象的工廠鞠值,不需要顯式指定它們的類彤恶。每個(gè)生成的工廠都能按照工廠模式提供對象声离。
單例模式Singleton Pattern
單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計(jì)模式之一抵恋。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式唤锉。
這種模式涉及到一個(gè)單一的類窿祥,該類負(fù)責(zé)創(chuàng)建自己的對象晒衩,同時(shí)確保只有單個(gè)對象被創(chuàng)建。這個(gè)類提供了一種訪問其唯一的對象的方式贝奇,可以直接訪問掉瞳,不需要實(shí)例化該類的對象浪漠。
注意:
1址愿、單例類只能有一個(gè)實(shí)例响谓。
2歌粥、單例類必須自己創(chuàng)建自己的唯一實(shí)例失驶。
3、單例類必須給所有其他對象提供這一實(shí)例擦耀。
意圖:保證一個(gè)類僅有一個(gè)實(shí)例眷蜓,并提供一個(gè)訪問它的全局訪問點(diǎn)吁系。
主要解決:一個(gè)全局使用的類頻繁地創(chuàng)建與銷毀汽纤。
何時(shí)使用:當(dāng)您想控制實(shí)例數(shù)目蕴坪,節(jié)省系統(tǒng)資源的時(shí)候背传。
如何解決:判斷系統(tǒng)是否已經(jīng)有這個(gè)單例,如果有則返回痴脾,如果沒有則創(chuàng)建明郭。
關(guān)鍵代碼:構(gòu)造函數(shù)是私有的薯定。
應(yīng)用實(shí)例:
1话侄、一個(gè)班級只有一個(gè)班主任年堆。
2变丧、Windows 是多進(jìn)程多線程的痒蓬,在操作一個(gè)文件的時(shí)候攻晒,就不可避免地出現(xiàn)多個(gè)進(jìn)程或線程同時(shí)操作一個(gè)文件的現(xiàn)象鲁捏,所以所有文件的處理必須通過唯一的實(shí)例來進(jìn)行给梅。
3破喻、一些設(shè)備管理器常常設(shè)計(jì)為單例模式盟榴,比如一個(gè)電腦有兩臺打印機(jī)擎场,在輸出的時(shí)候就要處理不能兩臺打印機(jī)打印同一個(gè)文件迅办。
優(yōu)點(diǎn):
1、在內(nèi)存里只有一個(gè)實(shí)例姨夹,減少了內(nèi)存的開銷磷账,尤其是頻繁的創(chuàng)建和銷毀實(shí)例(比如管理學(xué)院首頁頁面緩存)逃糟。
2绰咽、避免對資源的多重占用(比如寫文件操作)取募。
缺點(diǎn):沒有接口玩敏,不能繼承聊品,與單一職責(zé)原則沖突翻屈,一個(gè)類應(yīng)該只關(guān)心內(nèi)部邏輯伸眶,而不關(guān)心外面怎么樣來實(shí)例化厘贼。
使用場景:
1、要求生產(chǎn)唯一序列號岳掐。
2、WEB 中的計(jì)數(shù)器执解,不用每次刷新都在數(shù)據(jù)庫里加一次衰腌,用單例先緩存起來。
3尤泽、創(chuàng)建的一個(gè)對象需要消耗的資源過多,比如 I/O 與數(shù)據(jù)庫的連接等坯约。
注意事項(xiàng):getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時(shí)進(jìn)入造成 instance 被多次實(shí)例化闹丐。
策略模式
在策略模式(Strategy Pattern)中,一個(gè)類的行為或其算法可以在運(yùn)行時(shí)更改堕花。這種類型的設(shè)計(jì)模式屬于行為型模式。
在策略模式中壕曼,我們創(chuàng)建表示各種策略的對象和一個(gè)行為隨著策略對象改變而改變的 context 對象腮郊。策略對象改變 context 對象的執(zhí)行算法筹燕。
介紹
意圖:定義一系列的算法,把它們一個(gè)個(gè)封裝起來, 并且使它們可相互替換轧飞。
主要解決:在有多種算法相似的情況下衅鹿,使用 if...else 所帶來的復(fù)雜和難以維護(hù)。
何時(shí)使用:一個(gè)系統(tǒng)有許多許多類过咬,而區(qū)分它們的只是他們直接的行為塘安。
如何解決:將這些算法封裝成一個(gè)一個(gè)的類,任意地替換援奢。
關(guān)鍵代碼:實(shí)現(xiàn)同一個(gè)接口。
應(yīng)用實(shí)例:?1忍捡、諸葛亮的錦囊妙計(jì)集漾,每一個(gè)錦囊就是一個(gè)策略。 2具篇、旅行的出游方式埃疫,選擇騎自行車横蜒、坐汽車澎蛛,每一種旅行方式都是一個(gè)策略。 3、JAVA AWT 中的 LayoutManager屯吊。
優(yōu)點(diǎn):?1蔽介、算法可以自由切換薇组。 2炭菌、避免使用多重條件判斷尽楔。 3呕寝、擴(kuò)展性良好。
缺點(diǎn):?1、策略類會增多漱办。 2洞辣、所有策略類都需要對外暴露。
使用場景:?1谭羔、如果在一個(gè)系統(tǒng)里面有許多類话告,它們之間的區(qū)別僅在于它們的行為病线,那么使用策略模式可以動態(tài)地讓一個(gè)對象在許多行為中選擇一種行為。 2、一個(gè)系統(tǒng)需要動態(tài)地在幾種算法中選擇一種。 3、如果一個(gè)對象有很多的行為虐先,如果不用恰當(dāng)?shù)哪J街碛拢@些行為就只好使用多重的條件選擇語句來實(shí)現(xiàn)襟沮。
注意事項(xiàng):如果一個(gè)系統(tǒng)的策略多于四個(gè)丛忆,就需要考慮使用混合模式,解決策略類膨脹的問題纳鼎。
組合模式
組合模式(Composite Pattern),又叫部分整體模式斋荞,是用于把一組相似的對象當(dāng)作一個(gè)單一的對象柳刮。組合模式依據(jù)樹形結(jié)構(gòu)來組合對象凭舶,用來表示部分以及整體層次。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式仁卷,它創(chuàng)建了對象組的樹形結(jié)構(gòu)淆储。
這種模式創(chuàng)建了一個(gè)包含自己對象組的類还棱。該類提供了修改相同對象組的方式辞做。
我們通過下面的實(shí)例來演示組合模式的用法。實(shí)例演示了一個(gè)組織中員工的層次結(jié)構(gòu)框喳。
介紹
意圖:將對象組合成樹形結(jié)構(gòu)以表示"部分-整體"的層次結(jié)構(gòu)匙监。組合模式使得用戶對單個(gè)對象和組合對象的使用具有一致性。
主要解決:它在我們樹型結(jié)構(gòu)的問題中,模糊了簡單元素和復(fù)雜元素的概念训貌,客戶程序可以向處理簡單元素一樣來處理復(fù)雜元素,從而使得客戶程序與復(fù)雜元素的內(nèi)部結(jié)構(gòu)解耦冒窍。
何時(shí)使用:?1递沪、您想表示對象的部分-整體層次結(jié)構(gòu)(樹形結(jié)構(gòu))。 2综液、您希望用戶忽略組合對象與單個(gè)對象的不同款慨,用戶將統(tǒng)一地使用組合結(jié)構(gòu)中的所有對象。
如何解決:樹枝和葉子實(shí)現(xiàn)統(tǒng)一接口谬莹,樹枝內(nèi)部組合該接口檩奠。
關(guān)鍵代碼:樹枝內(nèi)部組合該接口,并且含有內(nèi)部屬性 List附帽,里面放 Component埠戳。
應(yīng)用實(shí)例:?1、算術(shù)表達(dá)式包括操作數(shù)蕉扮、操作符和另一個(gè)操作數(shù)整胃,其中,另一個(gè)操作符也可以是操作數(shù)喳钟、操作符和另一個(gè)操作數(shù)屁使。 2、在 JAVA AWT 和 SWING 中奔则,對于 Button 和 Checkbox 是樹葉蛮寂,Container 是樹枝。
優(yōu)點(diǎn):?1易茬、高層模塊調(diào)用簡單酬蹋。 2、節(jié)點(diǎn)自由增加。
缺點(diǎn):在使用組合模式時(shí)除嘹,其葉子和樹枝的聲明都是實(shí)現(xiàn)類写半,而不是接口,違反了依賴倒置原則尉咕。
使用場景:部分叠蝇、整體場景,如樹形菜單年缎,文件悔捶、文件夾的管理。
注意事項(xiàng):定義時(shí)為具體類单芜。