項目開發中發現問題、解決問題這個過程當中會出現不少問題,好比重複出現、某個問題的遺留,這些問題的本質就是設計模式。今天記錄設計模式的知識點。
java
在java以及其餘的面向對象設計模式中,類與類之間主要有6種關係,他們分別是:依賴、關聯、聚合、組合、繼承、實現。它們的耦合度依次加強。
依賴關係:
對於兩個相對獨立的對象,當一個對象負責構造另外一個對象的實例,或者依賴另外一個對象的服務時,這兩個對象之間主要體現爲依賴關係。
關聯關係:
分爲單向關聯和雙向關聯。在java中,單向關聯表現爲:類A當中使用了類B,其中類B是做爲類A的成員變量。雙向關聯表現爲:類A當中使用了類B做爲成員變量;同時類B中也使用了類A做爲成員變量。
聚合關係:
是關聯關係的一種,耦合度強於關聯,他們的代碼表現是相同的,僅僅是在語義上有所區別:關聯關係的對象間是相互獨立的,而聚合關係的對象之間存在着包容關係,他們之間是「總體-個體」的相互關係。
組合關係:
是一種耦合度更強的關聯關係。存在組合關係的類表示「總體-部分」的關聯關係,「總體」負責「部分」的生命週期,他們之間是共生共死的;而且「部分」單獨存在時沒有任何意義。
繼承:
表示類與類(或者接口與接口)之間的父子關係。
實現:
表示一個類實現一個或多個接口的方法。
算法
設計原則
數據庫
要點 | 定義 | 描述 |
單一職責原則 | 不要存在多於一個致使類變動的緣由。通俗的說,即一個類只負責一項職責。 | 問題由來:類T負責兩個不一樣的職責:職責P1,職責P2。當因爲職責P1需求發生改變而須要修改類T時,有可能會致使本來運行正常的職責P2功能發生故障。 解決方案:遵循單一職責原則。分別創建兩個類T一、T2,使T1完成職責P1功能,T2完成職責P2功能。這樣,當修改類T1時,不會使職責P2發生故障風險;同理,當修改T2時,也不會使職責P1發生故障風險。 |
里氏替換原則 | 定義1:若是對每個類型爲 T1的對象 o1,都有類型爲 T2 的對象o2,使得以 T1定義的全部程序 P 在全部的對象 o1 都代換成 o2 時,程序 P 的行爲沒有發生變化,那麼類型 T2 是類型 T1 的子類型。 定義2:全部引用基類的地方必須能透明地使用其子類的對象。 |
問題由來:有一功能P1,由類A完成。現須要將功能P1進行擴展,擴展後的功能爲P,其中P由原有功能P1與新功能P2組成。新功能P由類A的子類B來完成,則子類B在完成新功能P2的同時,有可能會致使原有功能P1發生故障。 解決方案:當使用繼承時,遵循里氏替換原則。類B繼承類A時,除添加新的方法完成新增功能P2外,儘可能不要重寫父類A的方法,也儘可能不要重載父類A的方法。 |
依賴倒置原則 | 高層模塊不該該依賴低層模塊,兩者都應該依賴其抽象;抽象不該該依賴細節;細節應該依賴抽象。 | 問題由來:類A直接依賴類B,假如要將類A改成依賴類C,則必須經過修改類A的代碼來達成。這種場景下,類A通常是高層模塊,負責複雜的業務邏輯;類B和類C是低層模塊,負責基本的原子操做;假如修改類A,會給程序帶來沒必要要的風險。 解決方案:將類A修改成依賴接口I,類B和類C各自實現接口I,類A經過接口I間接與類B或者類C發生聯繫,則會大大下降修改類A的概率。 |
接口隔離原則 | 客戶端不該該依賴它不須要的接口;一個類對另外一個類的依賴應該創建在最小的接口上。 | 問題由來:類A經過接口I依賴類B,類C經過接口I依賴類D,若是接口I對於類A和類B來講不是最小接口,則類B和類D必須去實現他們不須要的方法。 解決方案:將臃腫的接口I拆分爲獨立的幾個接口,類A和類C分別與他們須要的接口創建依賴關係。也就是採用接口隔離原則。 |
迪米特法則 | 一個對象應該對其餘對象保持最少的瞭解。 | 問題由來:類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另外一個類的影響也越大。 解決方案:儘可能下降類與類之間的耦合。 |
開閉原則 | 一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。 | 問題由來:在軟件的生命週期內,由於變化、升級和維護等緣由須要對軟件原有代碼進行修改時,可能會給舊代碼中引入錯誤,也可能會使咱們不得不對整個功能進行重構,而且須要原有代碼通過從新測試。 解決方案:當軟件須要變化時,儘可能經過擴展軟件實體的行爲來實現變化,而不是經過修改已有的代碼來實現變化。 |
設計模式編程
要點 | 定義 | 描述 |
單例模式 | 確保一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。 | 單例模式注意事項: 只能使用單例類提供的方法獲得單例對象,不要使用反射,不然將會實例化一個新對象。不要作斷開單例類對象與類中靜態引用的危險操做。多線程使用單例使用共享資源時,注意線程安全問題。 |
工廠方法模式 | 定義一個用於建立對象的接口,讓子類決定實例化哪個類,工廠方法使一個類的實例化延遲到其子類。 | 在工廠方法模式中,核心的工廠類再也不負責全部的對象的建立,而是將具體建立的工做交給子類去作。這個核心類則搖身一變,成爲了一個抽象工廠角色,僅負責給出具體工廠子類必須實現的接口,而不接觸哪個類應當被實例化這種細節。 |
抽象工廠模式 | 爲建立一組相關或相互依賴的對象提供一個接口,並且無需指定他們的具體類。 | 在如下狀況下,適用於工廠方法模式: (1) 當一個類不知道它所必須建立的對象的類的時候。 (2) 當一個類但願由它的子類來指定它所建立的對象的時候。 (3) 當類將建立對象的職責委託給多個幫助子類中的某一個,而且你但願將哪個幫助子類是代理者這一信息局部化的時候。 |
模版方法模式 | 定義一個操做中算法的框架,而將一些步驟延遲到子類中,使得子類能夠不改變算法的結構便可重定義該算法中的某些特定步驟。 | 子類能夠置換掉父類的可變部分,可是子類卻不能夠改變模板方法所表明的頂級邏輯。 每當定義一個新的子類時,不要按照控制流程的思路去想,而應當按照「責任」的思路去想。換言之,應當考慮哪些操做是必須置換掉的,哪些操做是能夠置換掉的,以及哪些操做是不能夠置換掉的。使用模板模式可使這些責任變得清晰。 |
建造者模式 | 將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。 | 與抽象工廠的區別:在建造者模式裏,有個指導者,由指導者來管理建造者,用戶是與指導者聯繫的,指導者聯繫建造者最後獲得產品。即建造模式能夠強制實行一種分步驟進行的建造過程。 建造模式是將複雜的內部建立封裝在內部,對於外部調用的人來講,只須要傳入建造者和建造工具,對於內部是如何建形成成品的,調用者無需關心。 在Java的應用中JavaMail使用到了該模式。 |
代理模式 | 爲其餘對象提供一種代理以控制對這個對象的訪問。 | 所謂代理,就是一我的或者機構表明另外一我的或者機構採起行動。在一些狀況下,一個客戶不想或者不可以直接引用一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。 |
原型模式 | 用原型實例指定建立對象的種類,並經過拷貝這些原型建立新的對象。 原型模式要求對象實現一個能夠「克隆」自身的接口,這樣就能夠經過複製一個實例對象自己來建立一個新的實例。這樣一來,經過原型實例建立新的對象,就再也不須要關心這個實例自己的類型,只要實現了克隆自身的方法,就能夠經過這個方法來獲取新的對象,而無須再去經過new來建立。 |
在Java語言裏深度克隆一個對象,經常能夠先使對象實現Serializable接口,而後把對象(實際上只是對象的拷貝)寫到一個流裏(序列化),再從流裏讀回來(反序列化),即可以重建對象。 原型模式的優勢 原型模式容許在運行時動態改變具體的實現類型。原型模式能夠在運行期間,由客戶來註冊符合原型接口的實現類型,也能夠動態地改變具體的實現類型,看起來接口沒有任何變化,但其實運行的已是另一個類實例了。由於克隆一個原型就相似於實例化一個類。 原型模式的缺點 原型模式最主要的缺點是每個類都必須配備一個克隆方法。配備克隆方法須要對類的功能進行通盤考慮,這對於全新的類來講不是很難,而對於已經有的類不必定很容易,特別是當一個類引用不支持序列化的間接對象,或者引用含有循環結構的時候。 |
中介者模式 | 用一箇中介者對象封裝一系列的對象交互,中介者使各對象不須要顯示地相互做用,從而使耦合鬆散,並且能夠獨立地改變它們之間的交互。 中介者模式的優勢 適當地使用中介者模式能夠避免同事類之間的過分耦合,使得各同事類之間能夠相對獨立地使用。 使用中介者模式能夠將對象間一對多的關聯轉變爲一對一的關聯,使對象間的關係易於理解和維護。 使用中介者模式能夠將對象的行爲和協做進行抽象,可以比較靈活的處理對象間的相互做用。 |
適用場景 在面向對象編程中,一個類必然會與其餘的類發生依賴關係,徹底獨立的類是沒有意義的。一個類同時依賴多個類的狀況也至關廣泛,既然存在這樣的狀況,說明,一對多的依賴關係有它的合理性,適當的使用中介者模式可使本來凌亂的對象關係清晰,可是若是濫用,則可能會帶來反的效果。通常來講,只有對於那種同事類之間是網狀結構的關係,纔會考慮使用中介者模式。能夠將網狀結構變爲星狀結構,使同事類之間的關係變的清晰一些。 中介者模式是一種比較經常使用的模式,也是一種比較容易被濫用的模式。對於大多數的狀況,同事類之間的關係不會複雜到混亂不堪的網狀結構,所以,大多數狀況下,將對象間的依賴關係封裝的同事類內部就能夠的,沒有必要非引入中介者模式。濫用中介者模式,只會讓事情變的更復雜。 |
命令模式 | 意圖:將一個請求封裝爲一個對象,從而可用不一樣的請求對客戶進行參數化;對請求排隊或記錄日誌,以及支持可撤銷的操做 動機:將」發出請求的對象」和」接收與執行這些請求的對象」分隔開來。 |
常見應用: 一、工做隊列,線程池,日程安排 二、日誌請求(系統恢復) 要點: 一、命令模式將發出請求的對象和執行請求的對象解耦 二、在被解耦的二者之間是經過命令對象進行溝通的。命令對象封裝了接收者和一個或一組動做 三、調用者經過調用命令對象的execute()發出請求,這會使得接收者的動做被調用 四、調用者能夠接受命令看成參數,甚至在運行時動態的進行 五、命令能夠支持撤銷,作法是實現一個undo()方法來回到execute()被執行前的狀態 六、宏命令是命令的一種簡單的延伸,容許調用多個命令。宏方法也能夠支持撤銷 七、實際操做時,很常見使用"聰明"命令對象,也就是直接實現了請求,而不是將工做委託給接受者(弊端?) 八、命令也能夠用來實現日誌和事物系統 |
責任鏈模式 | 使多個對象都有機會處理請求,從而避免了請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。 | 一個純的責任鏈模式要求一個具體的處理者對象只能在兩個行爲中選擇一個:一是承擔責任,而是把責任推給下家。不容許出現某一個具體處理者對象在承擔了一部分責任後又 把責任向下傳的狀況。 在一個純的責任鏈模式裏面,一個請求必須被某一個處理者對象所接收;在一個不純的責任鏈模式裏面,一個請求能夠最終不被任何接收端對象所接收。 純的責任鏈模式的實際例子很難找到,通常看到的例子均是不純的責任鏈模式的實現。有些人認爲不純的責任鏈根本不是責任鏈模式,這也許是有道理的。可是在實際的系統裏,純的責任鏈很難找到。若是堅持責任鏈不純便不是責任鏈模式,那麼責任鏈模式便不會有太大意義了。 |
裝飾模式 | 又名包裝(Wrapper)模式,裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案。 | 裝飾模式與類繼承的區別: 1) 裝飾模式是一種動態行爲,對已經存在類進行隨意組合,而類的繼承是一種靜態的行爲,一個類定義成什麼樣的,該類的對象便具備什麼樣的功能,沒法動態的改變。 2) 裝飾模式擴展的是對象的功能,不須要增長類的數量,而類繼承擴展是類的功能,在繼承的關係中,若是咱們想增長一個對象的功能,咱們只能經過繼承關係,在子類中增長兩個方法。 3) 裝飾與繼承比較圖: 4) 裝飾模式是在不改變原類文件和使用繼承的狀況下,動態的擴展一個對象的功能,它是經過建立一個包裝對象,也就是裝飾來包裹真是的對象。 5. 裝飾模式把對客戶端的調用委派給被裝飾的類,裝飾模式的關鍵在於這種擴展徹底透明的。 |
策略模式 | 定義一組算法,將每一個算法都封裝起來,而且使他們之間能夠互換。 策略模式的好處在於你能夠動態的改變對象的行爲。 |
策略模式屬於對象行爲型模式,主要針對一組算法,將每個算法封裝到具備共同接口的獨立的類中,從而使得它們能夠相互替換。策略模式使得算法能夠在不影響 到客戶端的狀況下發生變化。一般,策略模式適用於當一個應用程序須要實現一種特定的服務或者功能,並且該程序有多種實現方式時使用。 |
適配器模式 | 基於現有類所提供的服務,向客戶提供接口,以知足客戶的指望。 適配器模式的用意是要改變源的接口,以便於目標接口相容。缺省適配的用意稍有不一樣,它是爲了方便創建一個不平庸的適配器類而提供的一種平庸實現。 |
適配器模式的優勢 更好的複用性 系統須要使用現有的類,而此類的接口不符合系統的須要。那麼經過適配器模式就可讓這些功能獲得更好的複用。 更好的擴展性 在實現適配器功能的時候,能夠調用本身開發的功能,從而天然地擴展系統的功能。 適配器模式的缺點 過多的使用適配器,會讓系統很是零亂,不易總體進行把握。好比,明明看到調用的是A接口,其實內部被適配成了B接口的實現,一個系統若是太多出現這種狀況,無異於一場災難。所以若是不是頗有必要,能夠不使用適配器,而是直接對系統進行重構。 |
迭代器模式 | 提供一種方法訪問一個容器對象中各個元素,而又不暴露該對象的內部細節。 | 在jdk中,與迭代器相關的接口有兩個:Iterator 與 Iterable Iterator:迭代器,Iterator及其子類一般是迭代器自己的結構與方法; Iterable:可迭代的,那些想用到迭代器功能的其它類,如AbstractList HashMap等,須要實現該接口。 |
組合模式 | 將對象組合成樹形結構以表示‘部分-總體’的層次結構。組合模式使得用戶對單個對象和組合對象的使用具備一致性。 對象經過實現(繼承)統一的接口(抽象類),調用者對單一對象和組合對象的操做具備一致性。 |
經過實現組合模式,調用者對組合對象的操做與對單一對象的操做具備一致性。調用者不用關心這是組合對象仍是文件,也不用關心組合對象內部的具體結構,就能夠調用相關方法,實現功能。 |
觀察者模式 | 定義對象間一種一對多的依賴關係,使得當每個對象改變狀態,則全部依賴於它的對象都會獲得通知並自動更新。 觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知全部觀察者對象,使它們可以自動更新本身。 |
在JAVA語言的java.util庫裏面,提供了一個Observable類以及一個Observer接口,構成JAVA語言對觀察者模式的支持。 |
門面模式 | 外部與一個子系統的通訊必須經過一個統一的門面對象進行。 | 門面模式的優勢: ● 鬆散耦合 門面模式鬆散了客戶端與子系統的耦合關係,讓子系統內部的模塊能更容易擴展和維護。 ● 簡單易用 門面模式讓子系統更加易用,客戶端再也不須要了解子系統內部的實現,也不須要跟衆多子系統內部的模塊進行交互,只須要跟門面類交互就能夠了。 ● 更好的劃分訪問層次 經過合理使用Facade,能夠幫助咱們更好地劃分訪問的層次。有些方法是對系統外的,有些方法是系統內部使用的。把須要暴露給外部的功能集中到門面中,這樣既方便客戶端使用,也很好地隱藏了內部的細節。 |
備忘錄模式 | 在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。這樣就能夠將該對象恢復到原先保存的狀態。 | 備忘錄對象是一個用來存儲另一個對象內部狀態的快照的對象。備忘錄模式的用意是在不破壞封裝的條件下,將一個對象的狀態捕捉(Capture)住,並外部化,存儲起來,從而能夠在未來合適的時候把這個對象還原到存儲起來的狀態。備忘錄模式經常與命令模式和迭代子模式一同使用。 |
訪問者模式 | 封裝某些做用於某種數據結構中各元素的操做,它能夠在不改變數據結構的前提下定義做用於這些元素的新的操做。 訪問者模式是對象的行爲模式。訪問者模式的目的是封裝一些施加於某種數據結構元素之上的操做。一旦這些操做須要修改的話,接受這個操做的數據結構則能夠保持不變。 |
訪問者模式的優勢 好的擴展性 可以在不修改對象結構中的元素的狀況下,爲對象結構中的元素添加新的功能。 好的複用性 能夠經過訪問者來定義整個對象結構通用的功能,從而提升複用程度。 分離無關行爲 能夠經過訪問者來分離無關的行爲,把相關的行爲封裝在一塊兒,構成一個訪問者,這樣每個訪問者的功能都比較單一。 訪問者模式的缺點 對象結構變化很困難 不適用於對象結構中的類常常變化的狀況,由於對象結構發生了改變,訪問者的接口和訪問者的實現都要發生相應的改變,代價過高。 破壞封裝 訪問者模式一般須要對象結構開放內部數據給訪問者和ObjectStructrue,這破壞了對象的封裝性。 |
狀態模式 | 當一個對象的內在狀態改變時容許改變其行爲,這個對象看起來像是改變了其類。 狀態模式容許一個對象在其內部狀態改變的時候改變其行爲。這個對象看上去就像是改變了它的類同樣。 |
|
解釋器模式 | 給定一種語言,定義他的文法的一種表示,並定義一個解釋器,該解釋器使用該表示來解釋語言中句子。 | |
享元模式 | 複用咱們內存中已存在的對象,下降系統建立對象實例的性能消耗。 Flyweight在拳擊比賽中指最輕量級,即「蠅量級」或「雨量級」,這裏選擇使用「享元模式」的意譯,是由於這樣更能反映模式的用意。享元模式是對象的結構模式。享元模式以共享的方式高效地支持大量的細粒度對象。 |
享元模式採用一個共享來避免大量擁有相同內容對象的開銷。這種開銷最多見、最直觀的就是內存的損耗。享元對象能作到共享的關鍵是區份內蘊狀態(Internal State)和外蘊狀態(External State)。 |
橋樑模式 | 將抽象和實現解耦,使得二者能夠獨立地變化。 橋樑模式的用意是「將抽象化(Abstraction)與實現化(Implementation)脫耦,使得兩者能夠獨立地變化」。 |
橋樑模式在Java應用中的一個很是典型的例子就是JDBC驅動器。JDBC爲全部的關係型數據庫提供一個通用的界面。一個應用系統動態地選擇一個合適的驅動器,而後經過驅動器向數據庫引擎發出指令。這個過程就是將抽象角色的行爲委派給實現角色的過程。 |