一、設計模式之六大原則
1、單一職責原則(SRP:Single responsibility principle)
就一個類而言,應該僅有一個引起它變化的原因; 通俗的說,即一個類只負責一項職責
分析:
1、如果一個類承擔的職責過多,就等於把這些職責耦合在一起,一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力;
這種耦合會導致脆弱的設計,當變化發生時,設計會遭受到意想不到的破壞。
2、軟件設計真正要做的許多內容,就是發現職責並把那些職責相互分離。
3、如果你能夠想到多於一個的動機去改變一個類,那么這個類就具有多於一個的職責。
2、開放-封閉原則(OCP:Open Closed Principle)
是說軟件實體(類、模塊、函數等等)應該可以擴展,但是不可以修改;
特征1:對於擴展是開放的(Open for extension);
特征2:對於更改是封閉的(Closed for modification)。
分析:
1、在我們最初編寫代碼時,假設變化不會發生;當變化發生時,我們就創建抽象來隔離以后發生的同類變化。
2、面對需求,對程序的改動是通過增加新代碼進行的,而不是更改現有的代碼。
3、我們希望的是在開發工作展開不久就知道可能發生的變化,查明可能發生的變化所等待的時間越長,要創建正確的抽象就越難。
4、開發-封閉原則是面向對象設計的核心所在。 遵循此原則可以帶來 可維護、可擴展、可復用、靈活性好的好處;
開發人員應該僅對程序中呈現出頻繁變化的那些部分做出抽象,然而,對於應用呈現中的每個部分都刻意地進行抽象同樣不是一個好主意;
拒絕不成熟的抽象和抽象本身一樣重要。
3、依賴倒轉原則()
A.高層模塊不應該依賴低層模塊。兩個都應該依賴抽象
B.抽象不應該依賴細節,細節應該依賴抽象
分析:
1、就是要針對接口編程,不要對實現編程
無論主板、CUP、內存都是在針對接口設計的,如果針對實現來設計,內存就要應對到具體的某個品牌的主板,那就會出現換內存需要把主板也換了的尷尬;
2、高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象又如何理解呢?這個問題也可以這么問:為什么要叫倒置(倒轉)呢?
在面向過程的開發中,為了使用常用的代碼可以復用,一般都會把這些常用的代碼寫成許許多多函數的程序庫,這樣我們做新項目的時候,
就去調用這些函數就可以了。
例如:我們做的項目大多要訪問數據庫,所以我們就把數據庫的代碼寫成了函數,每次做新項目時就去調用這些函數,這也就是高層依賴於低層模塊了。
如果我們的高層模塊和低層模塊都依賴於抽象,具體一點就是依賴於接口或抽象類,只要接口夠穩定,那么任何一個的更改都不用擔心其他受到影響了。
4、里氏代換原則()
子類型必須能夠替換掉它們的父類型
既:一個軟件實體如果使用的是一個父類的話,那么一定適用於其子類,而其它察覺不出父類對象和子類對象的區別。
也就是說,在軟件里面,把父類都替換成它都替換成它的子類,程序的行為沒有變化。
分析:
1、正因為有這個原則,使得繼承復用成為了可能,
只有當子類可以替換掉父類,軟件單位的功能不受到影響時,父類才能真正被復用,而子類也能夠在父類的基礎上增加新的行為。
2、正是由於子類型的可替換性才使得使用父類類型的模塊在無需修改的情況下就可以擴展。
3、依賴倒轉其實可以說是面向對象設計的標志,用哪種語言來編寫程序不重要,如果編寫時考慮的都是如何針對抽象編程而不是針對細節編程,
即使程序中所有的依賴關系都是終止於抽象類或者接口,那就是面向對象的設計,反之那就是過程化的設計了。
5、迪米特法則(LKP:Least Knowledge Principle,又叫最少知識原則)
如果兩個類不必彼此直接通信,那么這兩個類就不應當直接的相互作用;如果其中一個類需要調用另一個類的某一個方法的話,可以通過第三者轉發這個調用。
分析:
1、在類的結構設計上,每一個類都應當盡量降低成員的訪問權限;
2、其根本思想是強調了類之間的松耦合;
3、類之間的耦合越弱,越有利於復用,一個處於弱耦合的類被修改,不會對有關系的類造成波及。
6、合成/聚合復用原則
盡量使用合成/聚合,盡量不要使用類繼承
優先使用對象的合成/聚合將有助於你保持每個類被封裝,並被集中在單個任務上;
這樣類和類繼承層次會保持較小規模,並且不太可能增長為不可能控制的龐然大物。
分析:
1、合成(Composition)和聚合(Aggregation)都是關聯的特殊種類;
2、聚合表示一種弱的“擁有”關系,體現的是A對象可以包含B對象,但B對象不是A對象的一部分;
3、合成則是一種強的“擁有”關系,體現了嚴格的部分和整體的關系,部分和整體的生命周期一樣
二、Java中的23種設計模式
1、單例模式——有些類也需要計划生育
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
所有類都有構造方法,不編碼則系統默認生成空的構造方法,若有顯示定義的構造方法,默認的構造方法就會失效。
分析:
1、通常我們可以讓一個全局變量使得一個對象被訪問,但它不能防止你實例化多個對象;
一個最好的辦法就是,讓類自身負責保存它的唯一實例;這個類可以保證沒有其他實例可以被創建,並且它可以提供一個訪問該實例的方法。
2、單例模式因為Singleton類封裝了它的唯一實例,這樣它可以嚴格地控制客戶怎樣訪問它以及何時訪問它。簡單地說就是對唯一實例的受控訪問。
實用類與單例區別?
比如實用類不保存狀態,僅提供一些靜態方法或靜態屬性讓你使用,而單例類是有狀態的;實用類不能用於繼承多態,而單例雖然實例唯一,卻是可以有子類來繼承。
實用類只不過是一些方法屬性的集合,而單例卻是有着唯一的對象實例。
懶漢式單例類:可以延遲加載,多線程不安全
//懶漢式
public class Singleton{ private static Singleton instance; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
餓漢式單例類:線程安全,加載類時就初始化完成不能延遲加載
public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
雙重檢查:多線程安全、延遲加載,同步耗時
public class Singleton { private static Singleton instance ; private Singleton(){} public static Singleton getInstance(){ if (instance == null){ synchronized (Singleton.class){ if (instance == null){ instance = new Singleton(); } } } return instance; } }
靜態內部類:多線程安全,延遲加載,比(雙重檢查)少耗時
public class Singleton { private Singleton(){} public static Singleton getInstance(){ return SingletonHolder.instance; } private static class SingletonHolder { private static Singleton instance = new Singleton(); } }
用緩存實現:線程安全,占用內存大
public class Singleton { private static final String KEY = "instance"; private static Map<String, Singleton> map = new HashMap<>(); private Singleton(){} public static Singleton getInstance(){ Singleton singleton ; if (map.get(KEY) == null){ singleton = new Singleton(); map.put(KEY, singleton); } else { singleton = map.get(KEY); } return singleton; } }
2、簡單工廠模式——代碼無錯就是優?
簡單工廠模式是由一個具體的類去創建其他類的實例,父類是相同的,父類是具體的。
應該考慮用一個單獨的類來做這個創造實例的過程,這就是工廠
分析:
1、簡單運算工廠類:
2、客戶端代碼:
所有在用簡單工廠的地方,都可以考慮用反射技術來去除switch或if,解除分支判斷帶來的耦合
3、工廠方法模式——雷鋒依然在人間
工廠方法模式是有一個抽象的父類定義公共接口,子類負責生成具體的對象,這樣做的目的是將類的實例化操作延遲到子類中完成。
定義一個用於創建對象的接口,讓子類決定實例化哪一個類; 使得一個類的實例化延遲到其子類。
分析:
1、簡單工廠模式的最大優點在於工廠類中包含了必要的邏輯判斷,根據客戶端的選擇條件動態實例化相關的類,對於客戶端來說,去除了與具體產品的依賴。
2、工廠方法模式實現時,客戶端需要決定實例化哪一個工廠來實現運算類,選擇判斷的問題還是存在的,
也就是說工廠方法把簡單工廠的內部邏輯判斷移動到了客戶端代碼來進行。你想要加功能,本來是改工廠類,而現在是修改客戶端。
4、抽象工廠模式——就不能不換DB嗎?
抽象工廠模式提供一個創建一系列相關或相互依賴對象的接口,而無須指定他們具體的類。
它針對的是有多個產品的等級結構。而工廠方法模式針對的是一個產品的等級結構。
分析:
1、好處一是 易於交換產品系列,由於具體工廠類,例如:IFactory factory = new AccessFactory(),在一個應用中只需要在初始化的時候出現一次,
這就使得改變一個應用的具體工廠變得非常容易,它只需要改變具體工廠即可使用不同的產品配置。
2、好處二是 讓具體的創建實例過程與客戶端分離,客戶端是通過它們的抽象接口操作實例,產品的具體類名也被具體工廠的實現分離,不會出現在客戶代碼中。
5、策略模式——商場促銷
它定義了算法家族,分別封裝起來,讓它們之間可以相互替換,此模式讓算法的變化,不會影響到使用算法的客戶
分析:
6、裝飾模式——穿什么有這么重要?
動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活
分析:
7、原型模式——簡歷復印
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象
原型模式其實就是從一個對象再創建另外一個可定制的對象,而且不需要指定任何創建的細節
分析:
8、模板方法模式——考題抄錯會做也白搭
定義一個操作中的算法的骨架,而將一些步驟延遲到子類中,模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟
分析:
9、外觀模式——牛市股票還會虧錢?
為子系統中的一組接口提供一個一致的界面,此模式定義了一個高層接口,這個接口使得這一子系統更加容易使用
分析:
10、建造者模式——好菜每回味不同
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示
分析:
11、觀察者模式——老板回來,我不知道
定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象;
這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己
分析:
12、狀態模式——無盡加班何時休
當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變了其類
主意解決:當控制一個對象狀態轉換的條件表達式過於復雜時的情況;把狀態的判斷邏輯移到表示不同狀態的一系列類當中,可以把復雜的判斷邏輯簡化
分析:
13、適配器模式——在NBA我需要翻譯
將一個類的接口轉換成客戶希望的另外一個接口;Adapter模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作
分析:
14、備忘錄模式——如果再回到從前
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態;這樣以后就可將該對象恢復到原先保存的狀態
分析:
15、組合模式——分公司=一部門
將對象組合成樹形結構以表示'部分-整體'的層次結構;組合模式使得用戶對於單個對象和組合對象的使用具有一致性
分析:
16、迭代器模式——想走?可以!先買票
提供一種方法順序訪問一個聚合對象中各個元素,而又不暴露該對象的內部表示
分析:
17、橋接模式——手機軟件何時統一
將抽象部分與它的實現部分分離,使它們都可以獨立地變化
什么叫抽象與它的實現分離,這並不是說,讓抽象類與其派生類分離,因為這沒有任何意義;實現指的是抽象類和它的派生類用來實現自己的對象
分析:
18、命令模式——烤肉串引來的思考
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支持可撤銷的操作
分析:
19、職責鏈模式——加薪非要老總批?
使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系;
將這個對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它為止
分析:
20、中介者模式——世界需要和平
用一個中介對象來封裝一系列的對象交互;
中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互
分析:
21、亨元模式——項目多也別傻做
運用共享技術有效地支持大量細粒度的對象
可以避免大量非常相似類的開銷
分析:
1、有時需要大量細粒度的類實例來表示數據,如果能發現這些實例除了幾個參數外基本上都是相同的,有時就能夠受大幅度地減少需要實例化的類的數量;
如果能把那些參數移到類實例的外面,在方法調用時將它們傳遞進來,就可以通過共享大幅度地減少單個實例的數目
22、解釋器模式——其實你不懂老板的心
給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子
分析:
1、當有一語言需要解釋執行,並且你可將該語言中的句子表示為一個抽象語法樹時,可使用解釋器模式
23、訪問者模式——男人和女人
表示一個作用於某對象結構中的各元素的操作;
它使你可以在不改變各元素的類的前期下定義作用於這些元素的新操作
分析:
24、代理模式——為別人做嫁衣
為其他對象提供一種代理以控制對這個對象的訪問
分析: