什么是設(shè)計模式
模式是在某情景下科汗,針對某問題的某種解決方案佛南。
模式必然應(yīng)用于一個重復(fù)出現(xiàn)的問題酿秸。問題包括了一個目標(biāo)和一組約束灭翔。
模式是被“發(fā)現(xiàn)”的,而不是被“創(chuàng)建”的辣苏。
良好的設(shè)計模式必須具備可復(fù)用缠局、可擴充、可維護三個特性考润。
模式分類
設(shè)計模式分為創(chuàng)建型、行為型和結(jié)構(gòu)性读处。
創(chuàng)建型
涉及到將對象實例化糊治,這類模式都提供一個方法,將客戶從所需要實例化的對象中解耦罚舱。包括:
工廠模式井辜,抽象工廠模式,單例模式管闷,生成器模式粥脚,原型模式。行為型
都涉及到類和對象如何交互及分配職責(zé)包个。包括:
模版模式刷允,命令模式冤留,迭代器模式,觀察者模式树灶,狀態(tài)模式纤怒,策略模式,訪問者模式天通,中介者模式 泊窘,備忘錄模式,責(zé)任鏈模式像寒,翻譯者模式烘豹。結(jié)構(gòu)型
可以讓你把類或?qū)ο蠼M合到更大的結(jié)構(gòu)中。用來描述類和對象如何組合以建立新的結(jié)構(gòu)或新的功能诺祸。包括:
裝飾者模式携悯,組合模式,適配器模式序臂,代理模式蚌卤,外觀模式,橋接模式奥秆,蠅量模式逊彭,
另一種分類方法將設(shè)計模式分為:處理類的模式,處理對象的模式构订。
類
模板模式侮叮,適配器模式,工廠模式悼瘾,翻譯者模式對象
裝飾者模式囊榜,組合模式,代理模式亥宿,外觀模式卸勺,橋接模式,蠅量模式烫扼,命令模式曙求,觀察者模式,狀態(tài)模式映企,策略模式悟狱,訪問者模式,中介者模式 堰氓,備忘錄模式挤渐,責(zé)任鏈模式,抽象工廠模式双絮,單例模式浴麻,生成器模式得问,原型模式。
設(shè)計原則
找到應(yīng)用中可能需要變化之處白胀,把它們獨立出來椭赋,不要和那些需要變化的代碼混在一起
所有的設(shè)計模式都提供一套方法讓系統(tǒng)中的某部分改變不會影響其他部分針對接口編程,而不是針對實現(xiàn)編程
利用接口代表每個行為或杠,行為的實現(xiàn)不會影響具體的使用該行為的對象哪怔。客戶可以利用接口(而不是具體類)引用每個具體對象向抢,減少了客戶和具體類之間的依賴认境。多用組合,少用集成
has-a比is-a具有更大的彈性和松耦合挟鸠。JAVA中類的繼承會限制復(fù)用潛力叉信。盡力實現(xiàn)交互對象之間的松耦合。
類應(yīng)該對擴展開放艘希,對修改關(guān)閉硼身。
在不修改基礎(chǔ)不變類的代碼的情況下進行功能的擴展。要依賴抽象覆享,不要依賴具體類(依賴倒置原則)
不能讓高層組件依賴底層組件佳遂。而且高層或底層組件,都應(yīng)該依賴于抽象撒顿。
倒置指的是原本高層依賴于低層丑罪,現(xiàn)在高低層都依賴中間抽象層。在設(shè)計中盡量避免依賴凤壁。好萊塢原則
別調(diào)用(打電話)我們吩屹,我們會調(diào)用(打電話給)你。
高層組件控制何時以及如何讓底層組件參與拧抖,底層組件絕對不可以直接調(diào)用高層組件煤搜,底層組件可以參與計算。高層和底層有點類似與模版模式里的父類和子類唧席,目的是不讓高層組建依賴低層組件擦盾,但是低層組件能夠被倒鉤進計算里。單一原則
一個類應(yīng)該只有一個引起變化的原因袱吆。保持簡單(Keep It Simple)
模式常常會產(chǎn)生一些額外的類和對象,加入更多層距淫,所以可能會引入復(fù)雜性并降低效率绞绒,你的目標(biāo)應(yīng)該是簡單,而不是“如何在這個問題中應(yīng)用模式”榕暇。學(xué)習(xí)管理軟件的復(fù)雜度和變化蓬衡,是程序員一生的課題喻杈。
模式是工具而不是規(guī)則,需要被適當(dāng)?shù)卣{(diào)用以符合你的需求狰晚。最少知識原則
只和你的密友談話筒饰。減少對象之間的交互。避免接觸一個對象時需要注意它所交互的類有哪些壁晒,是如何和這些類交互的瓷们。缺點是會制造更多的包裝類去處理和其它組件的溝通。以下為示例:
/**
*不推薦秒咐。從氣象站取得溫度計(Thermometer)后谬晕,再通過溫度計對象取得溫度。
*/
public float getTemperature() {
Thermometer thermometer = station.getThermometer();
return thermomeer.getTemperature();
}
/**
*推薦使用携取。將獲取溫度的操作封裝到氣象站中做攒钳,客戶不需要接觸過多數(shù)量的對象。
*/
pulic float getTemperature() {
return station.getTemperature();
}
具體模式
策略模式
定義了算法族雷滋,分別封裝起來不撑,讓它們之間可以互相替換,此模式讓算法的變換獨立于使用算法的客戶晤斩。適用對象組合的方式讓客戶可以選擇算法實現(xiàn)焕檬。可理解成是除了繼承之外的一種彈性替代方案尸昧,可組合不同對象來改變行為揩页。觀察者模式
定義為對象之間的一對多依賴,當(dāng)一個對象改變狀態(tài)時烹俗,他的所有依賴者都會收到通知并自動更新爆侣。Android為很多GUI(Graphical User Interface)控件(Button/ViewPager等)添加Listener監(jiān)聽,內(nèi)部使用了觀察者模式幢妄。裝飾者模式
動態(tài)的將責(zé)任附加到對象上兔仰。想要擴展功能,裝飾者提供了比繼承更有彈性的替代方案蕉鸳。通常裝飾者和被裝飾者具有相同的超類型乎赴,裝飾者在被裝飾者的行為之前/之后加上自行的行為進行修飾。I/O的API中用到了裝飾者模式:LineNumberInputSrream(BufferedInputStream(FileInputStream(InputSream))));工廠模式
定義了一個創(chuàng)捷對象的接口潮尝,但由子類決定要實例化的類是哪一個榕吼。工廠方法讓類把實例化推遲到子類。
簡單工廠模式:提供一個工廠負責(zé)制造多類型的產(chǎn)品勉失,工廠是全能類
工廠模式:多個工廠各自負責(zé)單一類型產(chǎn)品的構(gòu)建羹蚣,工廠為單一職責(zé)抽象工廠模式
提供一個接口,用于創(chuàng)建相關(guān)或依賴對象的家族乱凿,而不需要明確指定具體類顽素。 如果工廠的產(chǎn)品全部屬于同一個等級結(jié)構(gòu)則屬于工廠模式咽弦,如果來自多個等級結(jié)構(gòu)則屬于抽象工廠模式。單例模式
確保一個類只有一個實例胁出,并提供一個全局訪問點型型。可以在需要的時候才創(chuàng)建對象全蝶。Java中實現(xiàn)單例模式需要私有的構(gòu)造器闹蒜、一個靜態(tài)方法和一個靜態(tài)變量。沒有公開的構(gòu)造器裸诽,只能通過getInstance()
獲得單一對象嫂用。
JVM在加載類時會立即創(chuàng)建唯一的單例事例。命令模式
將“請求”封裝成對象丈冬,以便使用不同的請求嘱函、隊列或者日志來參數(shù)化其他對象。命令模式也支持可撤銷的操作埂蕊。
[應(yīng)用] 日程安排往弓,日志及事務(wù)請求,線程池蓄氧,工作隊列適配器模式
將一個類的接口轉(zhuǎn)換成客戶期望的另一個接口函似。適配器讓原來接口不兼容的類可以合作無間。外觀模式
提供了一個統(tǒng)一的接口喉童,用來訪問子系統(tǒng)中的一群接口撇寞。外觀定義了一個高層接口,讓子系統(tǒng)更容易使用堂氯。這個模式應(yīng)用非常之廣蔑担,比如Retrofit.create()方法就是使用了外觀模式。
外觀模式/適配器模式/裝飾者模式 三者區(qū)別:
適配器:將一個對象包裝起來以改變其接口
裝飾者:將一個對象包裝起來以增加新的行為和責(zé)任
外觀:將一群對象“包裝”起來以簡化接口
模板方法模式
在一個方法中定義一個算法的骨架咽白,而將一些步驟延遲到子類中啤握。模版方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法中的某些步驟晶框。
模版方法定義了一個算法的步驟排抬,并允許子類為一個或多個步驟提供實現(xiàn)。
比如Array的排序需要子類實現(xiàn)comparable接口的compareTo()方法.繼承Activity的頁面都是用了模版方法授段。模版方法的抽象類可定義具體方法蹲蒲,抽象方法和鉤子(子類可選擇是否覆蓋該默認實現(xiàn))。迭代器模式
提供一種方法順序訪問一個聚合對象中的各個元素侵贵,而又不暴露其內(nèi)部的表示届搁。把元素之間遍歷游走的順序交給迭代器而不是集合對象,讓集合更專注在它所應(yīng)該專注的事情上面。組合模式
將對象組合成樹形結(jié)構(gòu)來表現(xiàn)“整體/部分”層次結(jié)構(gòu)咖祭。組合能讓客戶以一致的方式處理個別對象以及對象組合。
組合包含組件蔫骂,組件有兩種:組合與葉節(jié)點元素么翰。類似View及ViewGroup的關(guān)系。狀態(tài)模式
允許對象在內(nèi)部狀態(tài)改變時改變它的行為辽旋,對象看起來好像修改了它的類浩嫌。將狀態(tài)封裝成為獨立的類,將動作委托到代表當(dāng)前狀態(tài)的對象补胚。如果使用的對象能夠完全改變它的行為码耐,就好像這個對象是從別的類實例化而來的,其實不是溶其。
Context會將行為委托給當(dāng)起對象骚腥。一般來講,當(dāng)狀態(tài)是固定的時候瓶逃,就適合將狀態(tài)轉(zhuǎn)換的流向放在context控制類中束铭;然而,當(dāng)狀態(tài)是更動態(tài)的時候厢绝,通常就會把狀態(tài)轉(zhuǎn)換的流向放在狀態(tài)類中契沫。擴展:UML類圖
就比如播放器MediaPlayer的各種狀態(tài)都是一個具有行為的對象。代理模式
為另一個對象提供一個替身或占位符以控制對這個對象的訪問昔汉。使用代理模式創(chuàng)建代表對象懈万,讓代表對象控制某對象的訪問,被代理的對象可以是遠程對象靶病、創(chuàng)建開銷大的對象或需要安全控制的對象会通。裝飾者為對象增加行為。代理是控制對象的訪問嫡秕。
遠程代理:管理客戶和遠程對象之間的交互渴语。遠程代理可以作為另一個JVM上對象的本地代表。代用代理的方法昆咽,會被代理利用網(wǎng)絡(luò)轉(zhuǎn)發(fā)到遠程執(zhí)行驾凶,并且結(jié)果會通過網(wǎng)絡(luò)返回給代理。
虛擬代理:控制訪問實例化開銷大的對象掷酗。虛擬代理作為創(chuàng)建開銷大的對象的代表调违。虛擬代理經(jīng)常知道我們真正需要一個對象的時候才創(chuàng)建它。當(dāng)對象再創(chuàng)建前和創(chuàng)建中時泻轰,由虛擬代理來扮演對象的替身技肩。對象創(chuàng)建后,代理就會將請求直接委托給對象。
保護代理:也稱動態(tài)代理虚婿⌒荩基于調(diào)用者控制對象方法的訪問∪蝗可根據(jù)訪問權(quán)限決定客戶可否訪問對象的代理至朗。
- 復(fù)合模式
復(fù)合模式在一個解決方案中結(jié)合兩個或多個模式,以解決一般或重復(fù)發(fā)生的問題剧浸。復(fù)合模式必須具有一般性锹引,適合解決許多問題才行。
MVC
Model-View-Controller(模型-視圖-控制)就是復(fù)合模式唆香,Android中的典型應(yīng)用是LiewView:(Model-ListView-Adapter);M層處理數(shù)據(jù)嫌变,業(yè)務(wù)邏輯等;V層處理界面的顯示結(jié)果躬它;C層起到橋梁的作用腾啥,聰明的將來自試圖的動作轉(zhuǎn)換成模型上的動作。
控制器把控制邏輯從視圖中分離冯吓,減少視圖的責(zé)任碑宴,讓模型和視圖之間解耦。
模式:
視圖和控制器實現(xiàn)了策略模式桑谍。視圖可以被調(diào)整使用不同的控制器提供的策略延柠,任何界面行為都委托給控制器處理,控制器負責(zé)和模型交互來傳遞用戶的請求锣披。
視圖和模型實現(xiàn)了觀察者模式 贞间。模型是被觀察者,視圖和控制器是觀察者雹仿。
視圖用了組合模式實現(xiàn)GUI增热。
橋接模式
將抽象部分與它的實現(xiàn)部分分離,使它們都可以獨立地變化胧辽。不止改變你的實現(xiàn)峻仇,也改變你的抽象。適合使用在需要跨越多個平臺的圖形和窗口系統(tǒng)上邑商。當(dāng)需要用不同的方式改變接口和實現(xiàn)時摄咆,橋接模式很好用。生成器模式
封裝一個產(chǎn)品的構(gòu)造過程人断,并需要按步驟構(gòu)造吭从。將一個復(fù)雜對象的創(chuàng)建過程封裝起來,允許對象通過多個步驟來創(chuàng)建恶迈,并且可以改變過程涩金。經(jīng)常被用來創(chuàng)建組合結(jié)構(gòu)。責(zé)任鏈模式
當(dāng)你想要讓一個以上的對象有計劃能夠處理某個請求的時候,就使用責(zé)任鏈模式步做。為某個請求創(chuàng)建一個對象鏈副渴,每個對象一次檢查此請求,并對其進行處理全度,或者將它傳給鏈中的下一個對象佳晶。將請求的發(fā)送者和接收者解耦。(okHttp, Fresco, RxJava都有使用)蠅量模式
如果想讓某個類的一個實例能用來提供許多“虛擬實例”讼载,就使用蠅量模式。的那個一個類又許多實例中跌,而這些實例能被同一方法控制的時候咨堤,可使用蠅量′龇可將需要“虛擬”對象的狀態(tài)集中管理一喘。減少運行時對象實例的個數(shù),節(jié)省內(nèi)存嗜暴。解釋器模式
為語言創(chuàng)建解釋器凸克。將每一個語法規(guī)則表示成一個類,方便于實現(xiàn)語言闷沥∥剑可以處理腳本語言和編程語言。要想解釋這種語言舆逃,就調(diào)用每個表達式類型的interpret()
方法蚂维,此方法需要傳入一個上下文Context
——也就是我們正在解析的語言字符串輸入流——然后進行對比并采取適當(dāng)?shù)膭幼鳌?/p>中介者模式
使用中介者模式來集中相關(guān)對象之間復(fù)雜的溝通和控制方式。每個對象都會在自己的狀態(tài)改變時路狮,告訴中介者虫啥。每個對象都會對中介者所發(fā)出的請求作出回應(yīng)。中介者內(nèi)包含了整個系統(tǒng)的控制邏輯奄妨。通過將對象之間彼此解耦涂籽,可以增加對象的復(fù)用性。通過將控制邏輯集中砸抛,可以簡化系統(tǒng)維護评雌。中介者常常被用來協(xié)調(diào)相關(guān)的GUI組件。備忘錄模式
存儲系統(tǒng)關(guān)鍵對象的重要狀態(tài)直焙,維護關(guān)鍵對象的封裝柳骄。用戶存儲狀態(tài)。將被儲存的狀態(tài)放在外面箕般,不要和關(guān)鍵對象混在一起耐薯,這可以幫助維護內(nèi)聚。在Java系統(tǒng)中,可以考慮使用序列化(serialization)機制存儲系統(tǒng)的狀態(tài)曲初。Activity及Fragment的saveInstanceState有用到体谒。原型模式
當(dāng)創(chuàng)建給定的類的實例的過程很昂貴或很復(fù)雜時,就使用原型模式臼婆。允許你通過復(fù)制現(xiàn)有的實例來創(chuàng)建新的實例(在Java中抒痒,這通常意味著使用clone()
方法或者反序列方法)“涔樱客戶的代碼在不知道要實例化何種特定類的情況下故响,可以制造新的實例。在一個復(fù)雜的類層次中颁独,但系統(tǒng)必須從其中的許多類型創(chuàng)建新對象時彩届,可以考慮原型。訪問者模式
當(dāng)你想要為一個對象的組合增加新的能力誓酒,且封裝并不重要時樟蠕,就使用訪問者模式。訪問者必須參觀組合內(nèi)的每個元素靠柑,在導(dǎo)游traverser
對象中寨辩,訪問者通過導(dǎo)游的引導(dǎo),收集組合中所有對象的狀態(tài)歼冰。一旦狀態(tài)被收集了靡狞,客戶就可以讓訪問者對象進行各種操作。允許你對組合結(jié)構(gòu)加入新的操作隔嫡,而無需改變結(jié)構(gòu)本身耍攘。
語法基礎(chǔ)
抽象,封裝畔勤,多態(tài)蕾各,繼承
多態(tài)
多態(tài)分為編譯時多態(tài)和運行時多態(tài)。其中編輯時多態(tài)是靜態(tài)的庆揪,主要是指方法的重載式曲,它是根據(jù)參數(shù)列表的不同來區(qū)分不同的函數(shù),通過編輯之后會變成兩個不同的函數(shù)缸榛,在運行時談不上多態(tài)吝羞。而運行時多態(tài)是動態(tài)的,它是通過動態(tài)綁定來實現(xiàn)的内颗,也就是我們所說的多態(tài)性钧排。Java實現(xiàn)運動時多態(tài)有三個必要條件:繼承、重寫均澳、向上轉(zhuǎn)型恨溜。
牢牢記住每個設(shè)計模式是沒有必要的符衔,但是這是必經(jīng)的一步。重要的是糟袁,要記住設(shè)計模式的思想判族。先看定義,再看思想项戴。
思想補充
雖然大多數(shù)設(shè)計模式都會增加類形帮,但是真正重要的是你暴露給使用者的類數(shù)目,設(shè)計模式能夠?qū)㈩~外增加的類隱藏起來周叮。但是這些新增的類有利于業(yè)務(wù)增長辩撑。在設(shè)計的過程中,折衷一直都是免不了的仿耽。
閱讀感受
- 在實際開發(fā)中會發(fā)現(xiàn)其中的設(shè)計模式合冀。
在寫了一點代碼之后學(xué)習(xí)設(shè)計模式會對這些模式有明確的實例關(guān)聯(lián),讓人有意識的去總結(jié)并在之后應(yīng)用這種模式氓仲。比如之前一直覺得哇這個功能類之間的架構(gòu)好牛逼啊,只知道具體應(yīng)用了繼承重寫重載等技巧和具體流程的邏輯得糜,但是真的沒有去進一步總結(jié)這種模式敬扛。 - 幫助開發(fā)過程中運用設(shè)計模式。
這次看設(shè)計模式會有一種總覽的感覺朝抖,應(yīng)該會讓之后寫代碼更有設(shè)計觀一點啥箭。這對于開發(fā)者理解和運用設(shè)計模式有很大幫助。而且這種學(xué)習(xí)沒有階層限制治宣,但是一定要相信急侥,你在學(xué)會一些入門知識以及業(yè)務(wù)工作后,會到達另一種狀態(tài)侮邀。每個類都不知道其他類的細節(jié)坏怪,但是多個類實現(xiàn)了一個具體的功能需求,神奇绊茧。