設(shè)計(jì)模式是軟件開(kāi)發(fā)人員在軟件開(kāi)發(fā)過(guò)程中面臨的一般問(wèn)題的解決方案。這些解決方案是眾多軟件開(kāi)發(fā)人員經(jīng)過(guò)相當(dāng)長(zhǎng)的一段時(shí)間的試驗(yàn)和錯(cuò)誤總結(jié)出來(lái)的捣染。
設(shè)計(jì)模式是一套被反復(fù)使用的、多數(shù)人知曉的、經(jīng)過(guò)分類(lèi)編目的芝加、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了重用代碼射窒、讓代碼更容易被他人理解藏杖、保證代碼可靠性。 毫無(wú)疑問(wèn)脉顿,設(shè)計(jì)模式于己于他人于系統(tǒng)都是多贏的蝌麸,設(shè)計(jì)模式使代碼編制真正工程化,設(shè)計(jì)模式是軟件工程的基石弊予,如同大廈的一塊塊磚石一樣祥楣。項(xiàng)目中合理地運(yùn)用設(shè)計(jì)模式可以完美地解決很多問(wèn)題,每種模式在現(xiàn)實(shí)中都有相應(yīng)的原理來(lái)與之對(duì)應(yīng)汉柒,每種模式都描述了一個(gè)在我們周?chē)粩嘀貜?fù)發(fā)生的問(wèn)題误褪,以及該問(wèn)題的核心解決方案,這也是設(shè)計(jì)模式能被廣泛應(yīng)用的原因碾褂。
一兽间、單例模式,比如只有數(shù)據(jù)庫(kù)連接的唯一一個(gè)實(shí)例
1正塌、單例類(lèi)只能有一個(gè)實(shí)例嘀略。
2恤溶、單例類(lèi)必須自己創(chuàng)建自己的唯一實(shí)例。
3帜羊、單例類(lèi)必須給所有其他對(duì)象提供這一實(shí)例咒程。
介紹
意圖:保證一個(gè)類(lèi)僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)讼育。
主要解決:一個(gè)全局使用的類(lèi)頻繁地創(chuàng)建與銷(xiāo)毀帐姻。
何時(shí)使用:當(dāng)您想控制實(shí)例數(shù)目,節(jié)省系統(tǒng)資源的時(shí)候奶段。
如何解決:判斷系統(tǒng)是否已經(jīng)有這個(gè)單例饥瓷,如果有則返回,如果沒(méi)有則創(chuàng)建痹籍。
關(guān)鍵代碼:構(gòu)造函數(shù)是私有的呢铆。
應(yīng)用實(shí)例:?1、一個(gè)黨只能有一個(gè)書(shū)記蹲缠。 2棺克、Windows 是多進(jìn)程多線程的,在操作一個(gè)文件的時(shí)候吼砂,就不可避免地出現(xiàn)多個(gè)進(jìn)程或線程同時(shí)操作一個(gè)文件的現(xiàn)象逆航,所以所有文件的處理必須通過(guò)唯一的實(shí)例來(lái)進(jìn)行。 3渔肩、一些設(shè)備管理器常常設(shè)計(jì)為單例模式因俐,比如一個(gè)電腦有兩臺(tái)打印機(jī),在輸出的時(shí)候就要處理不能兩臺(tái)打印機(jī)打印同一個(gè)文件周偎。
優(yōu)點(diǎn):?1抹剩、在內(nèi)存里只有一個(gè)實(shí)例,減少了內(nèi)存的開(kāi)銷(xiāo)蓉坎,尤其是頻繁的創(chuàng)建和銷(xiāo)毀實(shí)例(比如管理學(xué)院首頁(yè)頁(yè)面緩存)澳眷。 2、避免對(duì)資源的多重占用(比如寫(xiě)文件操作)蛉艾。
缺點(diǎn):沒(méi)有接口钳踊,不能繼承,與單一職責(zé)原則沖突勿侯,一個(gè)類(lèi)應(yīng)該只關(guān)心內(nèi)部邏輯拓瞪,而不關(guān)心外面怎么樣來(lái)實(shí)例化。
使用場(chǎng)景:?1助琐、要求生產(chǎn)唯一序列號(hào)祭埂。 2、WEB 中的計(jì)數(shù)器兵钮,不用每次刷新都在數(shù)據(jù)庫(kù)里加一次蛆橡,用單例先緩存起來(lái)舌界。 3、創(chuàng)建的一個(gè)對(duì)象需要消耗的資源過(guò)多泰演,比如 I/O 與數(shù)據(jù)庫(kù)的連接等呻拌。
注意事項(xiàng):getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時(shí)進(jìn)入造成 instance 被多次實(shí)例化。
二睦焕、工廠模式
介紹
意圖:定義一個(gè)創(chuàng)建對(duì)象的接口柏锄,讓其子類(lèi)自己決定實(shí)例化哪一個(gè)工廠類(lèi),工廠模式使其創(chuàng)建過(guò)程延遲到子類(lèi)進(jìn)行复亏。
主要解決:主要解決接口選擇的問(wèn)題。
何時(shí)使用:我們明確地計(jì)劃不同條件下創(chuàng)建不同實(shí)例時(shí)缭嫡。
如何解決:讓其子類(lèi)實(shí)現(xiàn)工廠接口缔御,返回的也是一個(gè)抽象的產(chǎn)品。
關(guān)鍵代碼:創(chuàng)建過(guò)程在其子類(lèi)執(zhí)行妇蛀。
應(yīng)用實(shí)例:?1耕突、您需要一輛汽車(chē),可以直接從工廠里面提貨评架,而不用去管這輛汽車(chē)是怎么做出來(lái)的眷茁,以及這個(gè)汽車(chē)?yán)锩娴木唧w實(shí)現(xiàn)。 2纵诞、Hibernate 換數(shù)據(jù)庫(kù)只需換方言和驅(qū)動(dòng)就可以上祈。
優(yōu)點(diǎn):?1、一個(gè)調(diào)用者想創(chuàng)建一個(gè)對(duì)象浙芙,只要知道其名稱就可以了登刺。 2、擴(kuò)展性高嗡呼,如果想增加一個(gè)產(chǎn)品纸俭,只要擴(kuò)展一個(gè)工廠類(lèi)就可以。 3南窗、屏蔽產(chǎn)品的具體實(shí)現(xiàn)揍很,調(diào)用者只關(guān)心產(chǎn)品的接口。
缺點(diǎn):每次增加一個(gè)產(chǎn)品時(shí)万伤,都需要增加一個(gè)具體類(lèi)和對(duì)象實(shí)現(xiàn)工廠窒悔,使得系統(tǒng)中類(lèi)的個(gè)數(shù)成倍增加,在一定程度上增加了系統(tǒng)的復(fù)雜度壕翩,同時(shí)也增加了系統(tǒng)具體類(lèi)的依賴蛉迹。這并不是什么好事。
使用場(chǎng)景:?1放妈、日志記錄器:記錄可能記錄到本地硬盤(pán)北救、系統(tǒng)事件荐操、遠(yuǎn)程服務(wù)器等,用戶可以選擇記錄日志到什么地方珍策。 2托启、數(shù)據(jù)庫(kù)訪問(wèn),當(dāng)用戶不知道最后系統(tǒng)采用哪一類(lèi)數(shù)據(jù)庫(kù)攘宙,以及數(shù)據(jù)庫(kù)可能有變化時(shí)屯耸。 3、設(shè)計(jì)一個(gè)連接服務(wù)器的框架蹭劈,需要三個(gè)協(xié)議疗绣,"POP3"、"IMAP"铺韧、"HTTP"多矮,可以把這三個(gè)作為產(chǎn)品類(lèi),共同實(shí)現(xiàn)一個(gè)接口哈打。
注意事項(xiàng):作為一種創(chuàng)建類(lèi)模式塔逃,在任何需要生成復(fù)雜對(duì)象的地方,都可以使用工廠方法模式料仗。有一點(diǎn)需要注意的地方就是復(fù)雜對(duì)象適合使用工廠模式湾盗,而簡(jiǎn)單對(duì)象,特別是只需要通過(guò) new 就可以完成創(chuàng)建的對(duì)象立轧,無(wú)需使用工廠模式格粪。如果使用工廠模式,就需要引入一個(gè)工廠類(lèi)肺孵,會(huì)增加系統(tǒng)的復(fù)雜度匀借。
三、策略者模式
介紹
意圖:定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換平窘。
主要解決:在有多種算法相似的情況下吓肋,使用 if...else 所帶來(lái)的復(fù)雜和難以維護(hù)。
何時(shí)使用:一個(gè)系統(tǒng)有許多許多類(lèi)瑰艘,而區(qū)分它們的只是他們直接的行為是鬼。
如何解決:將這些算法封裝成一個(gè)一個(gè)的類(lèi),任意地替換紫新。
關(guān)鍵代碼:實(shí)現(xiàn)同一個(gè)接口均蜜。
應(yīng)用實(shí)例:?1、諸葛亮的錦囊妙計(jì)芒率,每一個(gè)錦囊就是一個(gè)策略囤耳。 2、旅行的出游方式,選擇騎自行車(chē)充择、坐汽車(chē)德玫,每一種旅行方式都是一個(gè)策略。 3椎麦、JAVA AWT 中的 LayoutManager宰僧。
優(yōu)點(diǎn):?1、算法可以自由切換观挎。 2琴儿、避免使用多重條件判斷。 3嘁捷、擴(kuò)展性良好造成。
缺點(diǎn):?1、策略類(lèi)會(huì)增多雄嚣。 2谜疤、所有策略類(lèi)都需要對(duì)外暴露。
使用場(chǎng)景:?1现诀、如果在一個(gè)系統(tǒng)里面有許多類(lèi),它們之間的區(qū)別僅在于它們的行為履肃,那么使用策略模式可以動(dòng)態(tài)地讓一個(gè)對(duì)象在許多行為中選擇一種行為仔沿。 2、一個(gè)系統(tǒng)需要?jiǎng)討B(tài)地在幾種算法中選擇一種尺棋。 3封锉、如果一個(gè)對(duì)象有很多的行為,如果不用恰當(dāng)?shù)哪J奖烀@些行為就只好使用多重的條件選擇語(yǔ)句來(lái)實(shí)現(xiàn)成福。
注意事項(xiàng):如果一個(gè)系統(tǒng)的策略多于四個(gè),就需要考慮使用混合模式荆残,解決策略類(lèi)膨脹的問(wèn)題奴艾。
四、觀察者模式
介紹
意圖:定義對(duì)象間的一種一對(duì)多的依賴關(guān)系内斯,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí)蕴潦,所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。
主要解決:一個(gè)對(duì)象狀態(tài)改變給其他對(duì)象通知的問(wèn)題俘闯,而且要考慮到易用和低耦合潭苞,保證高度的協(xié)作睬涧。
何時(shí)使用:一個(gè)對(duì)象(目標(biāo)對(duì)象)的狀態(tài)發(fā)生改變响逢,所有的依賴對(duì)象(觀察者對(duì)象)都將得到通知,進(jìn)行廣播通知士聪。
如何解決:使用面向?qū)ο蠹夹g(shù),可以將這種依賴關(guān)系弱化蝗碎。
關(guān)鍵代碼:在抽象類(lèi)里有一個(gè) ArrayList 存放觀察者們湖笨。
應(yīng)用實(shí)例:?1、拍賣(mài)的時(shí)候衍菱,拍賣(mài)師觀察最高標(biāo)價(jià)赶么,然后通知給其他競(jìng)價(jià)者競(jìng)價(jià)。 2脊串、西游記里面悟空請(qǐng)求菩薩降服紅孩兒辫呻,菩薩灑了一地水招來(lái)一個(gè)老烏龜,這個(gè)烏龜就是觀察者琼锋,他觀察菩薩灑水這個(gè)動(dòng)作放闺。
優(yōu)點(diǎn):?1、觀察者和被觀察者是抽象耦合的缕坎。 2怖侦、建立一套觸發(fā)機(jī)制。
缺點(diǎn):?1谜叹、如果一個(gè)被觀察者對(duì)象有很多的直接和間接的觀察者的話匾寝,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間。 2荷腊、如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話艳悔,觀察目標(biāo)會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰女仰。 3猜年、觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對(duì)象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化疾忍。
使用場(chǎng)景:
一個(gè)抽象模型有兩個(gè)方面乔外,其中一個(gè)方面依賴于另一個(gè)方面。將這些方面封裝在獨(dú)立的對(duì)象中使它們可以各自獨(dú)立地改變和復(fù)用一罩。
一個(gè)對(duì)象的改變將導(dǎo)致其他一個(gè)或多個(gè)對(duì)象也發(fā)生改變杨幼,而不知道具體有多少對(duì)象將發(fā)生改變,可以降低對(duì)象之間的耦合度聂渊。
一個(gè)對(duì)象必須通知其他對(duì)象推汽,而并不知道這些對(duì)象是誰(shuí)。
需要在系統(tǒng)中創(chuàng)建一個(gè)觸發(fā)鏈歧沪,A對(duì)象的行為將影響B(tài)對(duì)象歹撒,B對(duì)象的行為將影響C對(duì)象……,可以使用觀察者模式創(chuàng)建一種鏈?zhǔn)接|發(fā)機(jī)制诊胞。
注意事項(xiàng):?1暖夭、JAVA 中已經(jīng)有了對(duì)觀察者模式的支持類(lèi)锹杈。 2、避免循環(huán)引用迈着。 3竭望、如果順序執(zhí)行,某一觀察者錯(cuò)誤會(huì)導(dǎo)致系統(tǒng)卡殼裕菠,一般采用異步方式咬清。
五、適配器模式
介紹
意圖:將一個(gè)類(lèi)的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口奴潘。適配器模式使得原本由于接口不兼容而不能一起工作的那些類(lèi)可以一起工作旧烧。
主要解決:主要解決在軟件系統(tǒng)中,常常要將一些"現(xiàn)存的對(duì)象"放到新的環(huán)境中画髓,而新環(huán)境要求的接口是現(xiàn)對(duì)象不能滿足的掘剪。
何時(shí)使用:?1、系統(tǒng)需要使用現(xiàn)有的類(lèi)奈虾,而此類(lèi)的接口不符合系統(tǒng)的需要夺谁。 2、想要建立一個(gè)可以重復(fù)使用的類(lèi)肉微,用于與一些彼此之間沒(méi)有太大關(guān)聯(lián)的一些類(lèi)匾鸥,包括一些可能在將來(lái)引進(jìn)的類(lèi)一起工作,這些源類(lèi)不一定有一致的接口碉纳。 3扫腺、通過(guò)接口轉(zhuǎn)換,將一個(gè)類(lèi)插入另一個(gè)類(lèi)系中村象。(比如老虎和飛禽,現(xiàn)在多了一個(gè)飛虎攒至,在不增加實(shí)體的需求下厚者,增加一個(gè)適配器,在里面包容一個(gè)虎對(duì)象迫吐,實(shí)現(xiàn)飛的接口库菲。)
如何解決:繼承或依賴(推薦)。
關(guān)鍵代碼:適配器繼承或依賴已有的對(duì)象志膀,實(shí)現(xiàn)想要的目標(biāo)接口熙宇。
應(yīng)用實(shí)例:?1、美國(guó)電器 110V溉浙,中國(guó) 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è)沒(méi)有關(guān)聯(lián)的類(lèi)一起運(yùn)行坎背。 2、提高了類(lèi)的復(fù)用房维。 3沼瘫、增加了類(lèi)的透明度。 4咙俩、靈活性好耿戚。
缺點(diǎn):?1、過(guò)多地使用適配器阿趁,會(huì)讓系統(tǒng)非常零亂膜蛔,不易整體進(jìn)行把握。比如脖阵,明明看到調(diào)用的是 A 接口皂股,其實(shí)內(nèi)部被適配成了 B 接口的實(shí)現(xiàn),一個(gè)系統(tǒng)如果太多出現(xiàn)這種情況命黔,無(wú)異于一場(chǎng)災(zāi)難呜呐。因此如果不是很有必要,可以不使用適配器悍募,而是直接對(duì)系統(tǒng)進(jìn)行重構(gòu)蘑辑。 2.由于 JAVA 至多繼承一個(gè)類(lèi),所以至多只能適配一個(gè)適配者類(lèi)坠宴,而且目標(biāo)類(lèi)必須是抽象類(lèi)洋魂。
使用場(chǎng)景:有動(dòng)機(jī)地修改一個(gè)正常運(yùn)行的系統(tǒng)的接口,這時(shí)應(yīng)該考慮使用適配器模式喜鼓。
注意事項(xiàng):適配器不是在詳細(xì)設(shè)計(jì)時(shí)添加的副砍,而是解決正在服役的項(xiàng)目的問(wèn)題。