前言
最近復(fù)習(xí)設(shè)計(jì)模式,花了不少時(shí)間,由于項(xiàng)目經(jīng)驗(yàn)還比較少,雖然看得懂,能理解,但鑒于個(gè)人比較偏實(shí)踐,不善于記憶,隔一段時(shí)間后,卻很快就忘記了o(╯□╰)o,到真正自己說(shuō)的時(shí)候也說(shuō)不出來(lái)……與其看別人的總結(jié),不如自己再根據(jù)別人的博客再思考整理一番,或許這樣比反復(fù)看的效果會(huì)好些吧。下面是根據(jù)別人的博客和以下幾點(diǎn)思考方式來(lái)重新整理的筆記:
1.這種模式怎么理解?(盡量簡(jiǎn)單易懂)
2.類(lèi)圖怎樣畫(huà)?(代碼怎樣寫(xiě))
3.舉個(gè)栗子?(使用場(chǎng)景)
4.使用這種模式有什么優(yōu)缺點(diǎn)?(遵循什么原則)
相關(guān)代碼示例地址: navyifanr/AndroidTrainingDemo/DesignPattern
設(shè)計(jì)模式六大原則
1.開(kāi)閉原則(Open Close Principle)
開(kāi)閉原則就是說(shuō)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。在程序需要進(jìn)行拓展的時(shí)候,不能去修改原有的代碼,實(shí)現(xiàn)一個(gè)熱插拔的效果。所以一句話(huà)概括就是:為了使程序的擴(kuò)展性好,易于維護(hù)和升級(jí)。想要達(dá)到這樣的效果,我們需要使用接口和抽象類(lèi),后面的具體設(shè)計(jì)中我們會(huì)提到這點(diǎn)。
參考:https://realm.io/cn/news/donn-felker-solid-part-2/
2.里氏代換原則(Liskov Substitution Principle)
里氏代換原則(Liskov Substitution Principle LSP)面向?qū)ο笤O(shè)計(jì)的基本原則之一。 里氏代換原則中說(shuō),任何基類(lèi)可以出現(xiàn)的地方,子類(lèi)一定可以出現(xiàn)。 LSP是繼承復(fù)用的基石,只有當(dāng)衍生類(lèi)可以替換掉基類(lèi),軟件單位的功能不受到影響時(shí),基類(lèi)才能真正被復(fù)用,而衍生類(lèi)也能夠在基類(lèi)的基礎(chǔ)上增加新的行為。里氏代換原則是對(duì)“開(kāi)-閉”原則的補(bǔ)充。實(shí)現(xiàn)“開(kāi)-閉”原則的關(guān)鍵步驟就是抽象化。而基類(lèi)與子類(lèi)的繼承關(guān)系就是抽象化的具體實(shí)現(xiàn),所以里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。—— From Baidu 百科
參考:https://realm.io/cn/news/donn-felker-solid-part-3/
3.依賴(lài)倒轉(zhuǎn)原則(Dependence Inversion Principle)
這個(gè)是開(kāi)閉原則的基礎(chǔ),具體內(nèi)容:真對(duì)接口編程,依賴(lài)于抽象而不依賴(lài)于具體。
參考:https://realm.io/cn/news/donn-felker-solid-part-5/
4.接口隔離原則(Interface Segregation Principle)
這個(gè)原則的意思是:使用多個(gè)隔離的接口,比使用單個(gè)接口要好。還是一個(gè)降低類(lèi)之間的耦合度的意思,從這兒我們看出,其實(shí)設(shè)計(jì)模式就是一個(gè)軟件的設(shè)計(jì)思想,從大型軟件架構(gòu)出發(fā),為了升級(jí)和維護(hù)方便。所以上文中多次出現(xiàn):降低依賴(lài),降低耦合。
參考:https://realm.io/cn/news/donn-felker-solid-part-4/
5.迪米特法則(最少知道原則)(Demeter Principle)
為什么叫最少知道原則,就是說(shuō):一個(gè)實(shí)體應(yīng)當(dāng)盡量少的與其他實(shí)體之間發(fā)生相互作用,使得系統(tǒng)功能模塊相對(duì)獨(dú)立。
6.合成復(fù)用原則(Composite Reuse Principle)
原則是盡量使用合成/聚合的方式,而不是使用繼承。
模式分類(lèi)
創(chuàng)建型:?jiǎn)卫J健⒑?jiǎn)單工廠(chǎng)模式、工廠(chǎng)方法模式、抽象工廠(chǎng)模式、建造者模式、原型模式;
結(jié)構(gòu)型:代理模式、適配器模式、裝飾器模式、橋接模式、組合模式、享元模式、外觀(guān)模式;
行為型:觀(guān)察者模式、模板方法模式、命令模式、狀態(tài)模式、職責(zé)鏈模式、解釋器模式 、中介者模式、訪(fǎng)問(wèn)者模式、策略模式、備忘錄模式、迭代器模式;
創(chuàng)建者模式
1.單例模式(Singleton)
(1)理解:
保證一個(gè)類(lèi)僅有一個(gè)實(shí)例,并提供一個(gè)獲得該實(shí)例的方法(必須是靜態(tài)方法,通常用getInstance命名)
(2)類(lèi)圖:
常使用的實(shí)現(xiàn)方法是這兩種,懶漢單例模式和DCL單例模式可參考,單例設(shè)計(jì)模式的N中Java實(shí)現(xiàn)方法
(3)栗子:
android中有很多系統(tǒng)級(jí)別的全局變量,如時(shí)間,輸入法,如InputMethodManager,賬戶(hù),狀態(tài)欄等,android中對(duì)這些都直接或者有些間接用到了單例模式。
(4)優(yōu)缺點(diǎn):
餓漢式:
優(yōu):線(xiàn)程安全,調(diào)用時(shí)反應(yīng)速度快,在類(lèi)加載的同時(shí)已經(jīng)創(chuàng)建好了一個(gè)靜態(tài)對(duì)象(創(chuàng)建的唯一對(duì)象);
缺:資源利用效率不高,可能該實(shí)例并不需要,但也被系統(tǒng)加載了,在一些場(chǎng)景下是無(wú)法使用的,比如,如果Single實(shí)例的創(chuàng)建依賴(lài)參數(shù)或配置文件,則在getInstance()之前必須調(diào)用某個(gè)方法來(lái)設(shè)置這些參數(shù),但在設(shè)置之前,可能已經(jīng)new了Single實(shí)例;static 內(nèi)部類(lèi)式:
優(yōu):線(xiàn)程安全,資源利用率高,不執(zhí)行g(shù)etInstance就不會(huì)被實(shí)例。
缺:第一次加載時(shí)反應(yīng)不快。
總之,單例模式,省去了new操作符,降低了系統(tǒng)內(nèi)存的使用頻率,減輕GC壓力;有些類(lèi)如交易所的核心交易引擎,控制著交易流程,如果該類(lèi)可以創(chuàng)建多個(gè)的話(huà),系統(tǒng)容易亂,使用單例模式,才能保證核心交易服務(wù)器獨(dú)立控制整個(gè)流程。
2.簡(jiǎn)單工廠(chǎng)模式
(1)理解
建立一個(gè)工廠(chǎng)類(lèi),對(duì)實(shí)現(xiàn)了同一接口的一些類(lèi)進(jìn)行實(shí)例的創(chuàng)建。
(2)類(lèi)圖
(3)栗子
出現(xiàn)了大量的產(chǎn)品需要?jiǎng)?chuàng)建,并且具有共同的接口時(shí)使用,但一般采用工廠(chǎng)方法模式。
(4)優(yōu)缺點(diǎn)
優(yōu):簡(jiǎn)單粗暴
缺:如果傳入的字符串有誤,不能正確創(chuàng)建對(duì)象,而且代碼比較臃腫,擴(kuò)展性不強(qiáng)。
3.工廠(chǎng)方法模式(Factory Method)
(1)理解
用于創(chuàng)建對(duì)象的接口,讓子類(lèi)決定實(shí)例化哪個(gè)類(lèi)。工廠(chǎng)方式模式使一個(gè)類(lèi)的實(shí)例化延遲到其子類(lèi)。
(2)類(lèi)圖
(3)栗子
如,Java的ThreadFactory。希望工廠(chǎng)與產(chǎn)品的種類(lèi)對(duì)客戶(hù)端保持透明,給客戶(hù)端提供一致的操作,另外一種是不同的工廠(chǎng)和產(chǎn)品可以提供客戶(hù)端不同的服務(wù)或功能。
(4)優(yōu)缺點(diǎn)
優(yōu):可派生多個(gè)產(chǎn)品,容易擴(kuò)展性,遵循單一職責(zé)、依賴(lài)倒置
缺:類(lèi)的創(chuàng)建依賴(lài)工廠(chǎng)類(lèi),也就是說(shuō),如果想要拓展程序,必須對(duì)工廠(chǎng)類(lèi)進(jìn)行修改,這違背了閉包原則,所以可以考慮使用抽象工廠(chǎng)模式。
4.抽象工廠(chǎng)模式(Abstract Factory)
(1)理解
創(chuàng)建多個(gè)工廠(chǎng)類(lèi),一旦需要增加新的功能,直接增加新的工廠(chǎng)類(lèi)就可以了,不需要修改之前的代碼。
(2)類(lèi)圖
(3)栗子
如Java的List接口,軟件的換皮膚功能;需要一個(gè)接口可以提供一個(gè)產(chǎn)品族,且不必知道產(chǎn)品的具體種類(lèi)
(4)優(yōu)缺點(diǎn)
優(yōu):遵循單一職責(zé)、依賴(lài)倒置、開(kāi)閉原則,當(dāng)需要擴(kuò)展一個(gè)功能時(shí),只需做一個(gè)實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)Sender接口,同時(shí)做一個(gè)工廠(chǎng)類(lèi),實(shí)現(xiàn)Provider接口,就OK了,無(wú)需去改動(dòng)現(xiàn)成的代碼。
注:
三種工廠(chǎng)模式的區(qū)別:
簡(jiǎn)單工廠(chǎng):用來(lái)生產(chǎn)同一等級(jí)結(jié)構(gòu)中的任意產(chǎn)品,對(duì)于增加新的產(chǎn)品,無(wú)能為力。
工廠(chǎng)方法:用來(lái)生產(chǎn)同一等級(jí)結(jié)構(gòu)中的固定產(chǎn)品,支持增加任意產(chǎn)品。
抽象工廠(chǎng):用來(lái)生產(chǎn)不同產(chǎn)品族(由不同產(chǎn)品組合成的一套產(chǎn)品)的全部產(chǎn)品,對(duì)于增加新的產(chǎn)品,無(wú)能為力;支持增加產(chǎn)品族。
5.建造者模式(Builder)
(1)理解
將一個(gè)復(fù)雜對(duì)象的構(gòu)建和它的表示分離,使得同樣的創(chuàng)建過(guò)程可以創(chuàng)建不同的表示。
工廠(chǎng)類(lèi)模式提供的是創(chuàng)建單個(gè)類(lèi)的模式,而建造者模式則是將各種產(chǎn)品集中起來(lái)進(jìn)行管理,用來(lái)創(chuàng)建復(fù)合對(duì)象,所謂復(fù)合對(duì)象就是指某個(gè)類(lèi)具有不同的屬性,其實(shí)建造者模式就是前面抽象工廠(chǎng)模式和最后的Test結(jié)合起來(lái)得到的。
(2)類(lèi)圖
或許,《Effective Java》的例子更好,遇到多個(gè)構(gòu)造參數(shù)時(shí)要考慮用構(gòu)建器(Builder模式):
class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
//對(duì)象的必選參數(shù)
private final int servingSize;
private final int servings;
//對(duì)象的可選參數(shù)的缺省值初始化
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
//只用少數(shù)的必選參數(shù)作為構(gòu)造器的函數(shù)參數(shù)
public Builder(int servingSize,int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
//使用方式
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100)
.sodium(35).carbohydrate(27).build();
System.out.println(cocaCola);
}
(3)栗子
android中大量的使用對(duì)話(huà)框組件,它的調(diào)用方法就是構(gòu)建,拼接,表示。
Builder通過(guò)setTitle(),setMessage(),setIcon()等方法在create()中構(gòu)造出一個(gè)AlertDialogInstance對(duì)象。
然后客戶(hù)可以把把AlertDialogInstance對(duì)象show出去。
附: Android用建造者模式實(shí)現(xiàn)一個(gè)新功能引導(dǎo)頁(yè)
(4)優(yōu)缺點(diǎn)
優(yōu):復(fù)用對(duì)象,避免不斷new構(gòu)造者增加開(kāi)銷(xiāo);將很多功能集成到一個(gè)類(lèi)里,創(chuàng)造出比較復(fù)雜的東西。
6.原型模式
(1)理解
用原型實(shí)例指定創(chuàng)建對(duì)象的種類(lèi),并且通過(guò)拷貝這些原型創(chuàng)建新的對(duì)象。
(2)類(lèi)圖
略
注:克隆操作分深拷貝和淺拷貝,淺拷貝說(shuō)白了就是把原對(duì)象所有的值和引用直接賦給新對(duì)象。深拷貝則不僅把原對(duì)象的值賦給新對(duì)象,而且會(huì)把原對(duì)象的引用對(duì)象也重新創(chuàng)建一遍再賦給新對(duì)象。
(3)栗子
如Java的Cloneable接口。
使用場(chǎng)景:a.對(duì)象的創(chuàng)建非常復(fù)雜,可以使用原型模式快捷的創(chuàng)建對(duì)象。b.在運(yùn)行過(guò)程中不知道對(duì)象的具體類(lèi)型,可使用原型模式創(chuàng)建一個(gè)相同類(lèi)型的對(duì)象,或者在運(yùn)行過(guò)程中動(dòng)態(tài)的獲取到一個(gè)對(duì)象的狀態(tài)。
(4)優(yōu)缺點(diǎn)
缺:每個(gè)原型的子類(lèi)都必須實(shí)現(xiàn)Cloneable接口,這個(gè)實(shí)現(xiàn)起來(lái)有時(shí)候比較困難。
詳細(xì):原型模式詳解(clone方法源碼的簡(jiǎn)單剖析)
持續(xù)更新,整理得不對(duì)的地方,還望指正!
參考:
1.Java之美[從菜鳥(niǎo)到高手演變]之設(shè)計(jì)模式-終點(diǎn)
2.Android設(shè)計(jì)模式系列-謙虛天下
3.設(shè)計(jì)模式系列-Tony Chen
4.設(shè)計(jì)模式詳解-左瀟龍
5.設(shè)計(jì)模式UML類(lèi)圖