軟考架構師(9)——設計模式


全文鏈接:https://www.cnblogs.com/nullering/p/9684820.html

設計模式深入內容較多,我這里只是列舉一些考試中的概念,如果大家想深入理解一下可以參考這個大佬的博客:https://www.cnblogs.com/zuoxiaolong/category/509144.html

一:概述

概念:設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。

設計模式具有“適應需求變化”的優點。

基本原則:模塊應對外擴展開放,而對修改關閉,要針對接口,而不是組合,抽象不應該依賴於細節,細節應當依賴於抽象

基本分類:共23種模式 3種分類 創建型5 結構型7 行為型11

二:創建型5種 相公生原子

對象實例化的模式,創建型模式用於解耦對象的實例化過程。

抽象工廠模式(Abstract Factory Pattern) 

定義:(為創建一組相關或相互依賴的對象提供一個接口,而且無須指定它們的具體類。)  

   所謂抽象工廠模式就是提供一個接口,用於創建相關或者依賴對象的家族,而不需要明確指定具體類。他允許客戶端使用抽象的接口來創建一組相關的產品,而不需要關系實際產出的具體產品是什么。這樣一來,客戶就可以從具體的產品中被解耦。它的優點是隔離了具體類的生成,使得客戶端不需要知道什么被創建了,而缺點就在於新增新的行為會比較麻煩,因為當添加一個新的產品對象時,需要更加需要更改接口及其下所有子類。

工廠方法模式

定義:(定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。)

  工廠方法模式非常符合“開閉原則”,當需要增加一個新的產品時,我們只需要增加一個具體的產品類和與之對應的具體工廠即可,無須修改原有系統。同時在工廠方法模式中用戶只需要知道生產產品的具體工廠即可,無須關系產品的創建過程,甚至連具體的產品類名稱都不需要知道。雖然他很好的符合了“開閉原則”,但是由於每新增一個新產品時就需要增加兩個類,這樣勢必會導致系統的復雜度增加。

 

生成器模式(Builder Pattern) 

定義:(將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。)

使用場景:

● 相同的方法,不同的執行順序,產生不同的事件結果時,可以采用建造者模式。
● 多個部件或零件,都可以裝配到一個對象中,但是產生的運行結果又不相同時,則可以使用該模式。
● 產品類非常復雜,或者產品類中的調用順序不同產生了不同的效能,這個時候使用建造者模式非常合適。
 
 

原型模式(Prototype Pattern)

定義:(用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。)

 

單子模式 (Singleton Pattern)

定義:(確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。)

使用場景:

● 要求生成唯一序列號的環境;
● 在整個項目中需要一個共享訪問點或共享數據,例如一個Web頁面上的計數器,可以不用把每次刷新都記錄到數據庫中,使用單例模式保持計數器的值,並確保是線程安全的;
● 創建一個對象需要消耗的資源過多,如要訪問IO和數據庫等資源;
● 需要定義大量的靜態常量和靜態方法(如工具類)的環境,可以采用單例模式(當然,也可以直接聲明為static的方式)。

  當系統中只需要一個實例對象或者系統中只允許一個公共訪問點,除了這個公共訪問點外,不能通過其他訪問點訪問該實例時,可以使用單例模式。

  單例模式的主要優點就是節約系統資源、提高了系統效率,同時也能夠嚴格控制客戶對它的訪問。也許就是因為系統中只有一個實例,這樣就導致了單例類的職責過重,違背了“單一職責原則”,同時也沒有抽象類,所以擴展起來有一定的困難。

實現:

1)懶漢式——線程不安全

2)餓漢式——線程安全:

采取直接實例化 uniqueInstance 的方式就不會產生線程不安全問題。

但是直接實例化的方式也丟失了延遲實例化帶來的節約資源的好處。

 

3)懶漢式——線程安全

4)雙重校驗鎖——線程安全

5)靜態內部類

6)枚舉

 三:結構型7種 喬裝打扮想外住

 把類或對象結合在一起形成一個更大的結構。

橋模式(Bridge Pattern)

定義:(將抽象和實現解耦,使得兩者可以獨立地變化。)

使用場景:

● 不希望或不適用使用繼承的場景
● 接口或抽象類不穩定的場景
● 重用性要求較高的場景
 
注意:

發現類的繼承有N層時,可以考慮使用橋梁模式。橋梁模式主要考慮如何拆分抽象和實現。

 

裝飾(Decorator)模式

定義:(動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更為靈活。)

使用場景:

● 需要擴展一個類的功能,或給一個類增加附加功能。
● 需要動態地給一個對象增加功能,這些功能可以再動態地撤銷。
● 需要為一批的兄弟類進行改裝或加裝功能,當然是首選裝飾模式。

 

代理模式(Proxy Pattern) 

定義:(為其他對象提供一種代理以控制對這個對象的訪問。)

 

適配器模式(Adapter Pattern)

定義:(將一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。)

使用場景:

你有動機修改一個已經投產中的接口時,適配器模式可能是最適合你的模式。比如系統擴展了,需要使用一個已有或新建立的類,但這個類又不符合系統的接口,怎么辦?使用適配器模式,這也是我們例子中提到的。

注意事項:

詳細設計階段不要考慮使用適配器模式,使用主要場景為擴展應用中。

 

享元模式(Flyweight Pattern)

定義:(使用共享對象可有效地支持大量的細粒度的對象。)

使用場景:

● 系統中存在大量的相似對象。
● 細粒度的對象都具備較接近的外部狀態,而且內部狀態與環境無關,也就是說對象沒有特定身份。
● 需要緩沖池的場景。

注意:

● 享元模式是線程不安全的,只有依靠經驗,在需要的地方考慮一下線程安全,在大部分場景下不用考慮。對象池中的享元對象盡量多,多到足夠滿足為止。

● 性能安全:外部狀態最好以java的基本類型作為標志,如String,int,可以提高效率。

 

外觀模式(Facade Pattern)

定義:(要求一個子系統的外部與其內部的通信必須通過一個統一的對象進行。門面模式提供一個高層次的接口,使得子系統更易於使用。)

使用場景:

● 為一個復雜的模塊或子系統提供一個供外界訪問的接口
● 子系統相對獨立——外界對子系統的訪問只要黑箱操作即可
● 預防低水平人員帶來的風險擴散
注意:
●一個子系統可以有多個門面
●門面不參與子系統內的業務邏輯

 

 

組合(Composite)模式

定義:(將對象組合成樹形結構以表示“部分-整體”的層次結構,使得用戶對單個對象和組合對象的使用具有一致性。)

使用場景:

● 維護和展示部分-整體關系的場景,如樹形菜單、文件和文件夾管理。
● 從一個整體中能夠獨立出部分模塊或功能的場景。

注意:

只要是樹形結構,就考慮使用組合模式。

 

四:行為型11種 (訪問者通過觀察備忘錄模版狀態命令中介者迭代解釋職責策略)

類和對象如何交互,及划分責任和算法。

訪問者模式(Visitor Pattern)

定義:(封裝一些作用於某種數據結構中的各元素的操作,它可以在不改變數據結構的前提下定義作用於這些元素的新的操作。)

 

 

觀察者模式(Observer Pattern)

定義:(定義對象間一種一對多的依賴關系,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新。)

使用場景:

● 關聯行為場景。需要注意的是,關聯行為是可拆分的,而不是“組合”關系。
● 事件多級觸發場景。
● 跨系統的消息交換場景,如消息隊列的處理機制。

注意:

● 廣播鏈的問題

在一個觀察者模式中最多出現一個對象既是觀察者也是被觀察者,也就是說消息最多轉發一次(傳遞兩次)。

● 異步處理問題

觀察者比較多,而且處理時間比較長,采用異步處理來考慮線程安全和隊列的問題。

 

 備忘錄模式(Memento Pattern)

定義:(在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以后就可將該對象恢復到原先保存的狀態。)

使用場景:

● 需要保存和恢復數據的相關狀態場景。
● 提供一個可回滾(rollback)的操作。
● 需要監控的副本場景中。
● 數據庫連接的事務管理就是用的備忘錄模式。

注意:

●備忘錄的生命期

●備忘錄的性能

   不要在頻繁建立備份的場景中使用備忘錄模式(比如一個for循環中)。

 

 

模版模式(Template Method Pattern)

定義:(定義一個操作中的算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。)

 

 

狀態模式

定義:(當一個對象內在狀態改變時允許其改變行為,這個對象看起來像改變了其類。)

使用場景:

● 行為隨狀態改變而改變的場景
這也是狀態模式的根本出發點,例如權限設計,人員的狀態不同即使執行相同的行為結果也會不同,在這種情況下需要考慮使用狀態模式。
● 條件、分支判斷語句的替代者

注意:

狀態模式適用於當某個對象在它的狀態發生改變時,它的行為也隨着發生比較大的變化,也就是說在行為受狀態約束的情況下可以使用狀態模式,而且使用時對象的狀態最好不要超過5個。

 

命令模式

定義:(將一個請求封裝成一個對象,從而讓你使用不同的請求把客戶端參數化,對請求排隊或者記錄請求日志,可以提供命令的撤銷和恢復功能。)

 

 

中介者(mediator)模式

定義:(用一個中介對象封裝一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。)

使用場景:

中介者模式適用於多個對象之間緊密耦合的情況,緊密耦合的標准是:在類圖中出現了蜘蛛網狀結構,即每個類都與其他的類有直接的聯系。

 

 

迭代器模式(Iterator Pattern)

定義:(它提供一種方法訪問一個容器對象中各個元素,而又不需暴露該對象的內部細節。)

 

解釋器模式(Interpreter Pattern)

定義:(給定一門語言,定義它的文法的一種表示,並定義一個解釋器,該解釋器使用該表示來解釋語言中的句子。)

使用場景:

● 重復發生的問題可以使用解釋器模式

● 一個簡單語法需要解釋的場景

 

注意:

盡量不要在重要的模塊中使用解釋器模式,否則維護會是一個很大的問題。在項目中可以使用shell、JRuby、Groovy等腳本語言來代替解釋器模式,彌補Java編譯型語言的不足。

 

職責鏈模式

定義:(使多個對象都有機會處理請求,從而避免了請求的發送者和接受者之間的耦合關系。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它為止。)

 

 

 

策略模式(Strategy Pattern)

定義:(定義一組算法,將每個算法都封裝起來,並且使它們之間可以互換。)

使用場景:

● 多個類只有在算法或行為上稍有不同的場景。

● 算法需要自由切換的場景。

● 需要屏蔽算法規則的場景。

注意事項:具體策略數量超過4個,則需要考慮使用混合模式

 

最后 加上兩個 mvc 既是架構風格 也可以是 設計模式
篩選器模式 java .net 在注冊訪問時 常用的方法
1、策略 2、觀察者、3、裝飾 4、工廠 5、單子 6、命令 7、適配器 8模版 9、組合 10、狀態
設計模式 是干啥的 ? 是 對於一類問題的通用解決辦法
四要素 :模式名稱、問題、解決方案、效果

 

五:軟件設計原則

1.單一職責                                                                                                                                                     

一個類,只有一個引起它變化的原因。應該只有一個職責。每一個職責都是變化的一個軸線,如果一個類有一個以上的職責,這些職責就耦合在了一起。這會導致脆弱的設計。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響復用性

簡單通俗的來說:一個類只負責一項職責。

 

遵循單一職責原的優點有:

  • 可以降低類的復雜度,一個類只負責一項職責,其邏輯肯定要比負責多項職責簡單的多;

  • 提高類的可讀性,提高系統的可維護性;

  • 變更引起的風險降低,變更是必然的,如果單一職責原則遵守的好,當修改一個功能時,可以顯著降低對其他功能的影響。

需要說明的一點是單一職責原則不只是面向對象編程思想所特有的,只要是模塊化的程序設計,都適用單一職責原則。

單一職責看似簡單,實際上在實際運用過程中,會發現真的會出現很多職責擴展的現象,這個時候采用直接違反還會方法上遵循還是完全遵循單一職責原則還是取決於當前業務開發的人員的技能水平和這個需求的時間,如果技能水平不足,肯定會簡單的if else 去解決,不會想什么原則,直接實現功能就好了,這也是為什么在很多小公司會發現代碼都是業務堆起來的,當然也有好的小公司代碼是寫的好的,這個也是不可否認的。不過不管采用什么方式解決,心中至少要知道有幾種解決方法。

 

2.里氏替換原則 (Liskov Substitution Principle)                                                                       

里氏代換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。 LSP是繼承復用的基石,只有當衍生類可以替換掉基類,軟件單位的功能不受到影響時,基類才能真正被復用,而衍生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對“開-閉”原則的補充。實現“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關系就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規范。

 

 3.依賴倒置原則 (Dependence Inversion Principle)                                                       

所謂依賴倒置原則(Dependence Inversion Principle)就是要依賴於抽象,不要依賴於具體。實現開閉原則的關鍵是抽象化,並且從抽象化導出具體化實現,如果說開閉原則是面向對象設計的目標的話,那么依賴倒轉原則就是面向對象設計的主要手段。

定義:高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。

通俗點說:要求對抽象進行編程,不要對實現進行編程,這樣就降低了客戶與實現模塊間的耦合。

傳遞依賴關系有三種方式,以上的例子中使用的方法是接口傳遞,另外還有兩種傳遞方式:構造方法傳遞和setter方法傳遞,相信用過Spring框架的,對依賴的傳遞方式一定不會陌生。

在實際編程中,我們一般需要做到如下3點:

  • 低層模塊盡量都要有抽象類或接口,或者兩者都有。【可能會被人用到的】

  • 變量的聲明類型盡量是抽象類或接口。

  • 使用繼承時遵循里氏替換原則。

依賴倒置原則的核心就是要我們面向接口編程,理解了面向接口編程,也就理解了依賴倒置。

 4.接口隔離原則 (Interface Segregation Principle)                                                                 

其原則字面的意思是:使用多個隔離的接口,比使用單個接口要好。本意降低類之間的耦合度,而設計模式就是一個軟件的設計思想,從大型軟件架構出發,為了升級和維護方便。所以上文中多次出現:降低依賴,降低耦合。

原定義:客戶端不應該依賴它不需要的接口;一個類對另一個類的依賴應該建立在最小的接口上。 

接口隔離原則的含義是:建立單一接口,不要建立龐大臃腫的接口,盡量細化接口,接口中的方法盡量少。也就是說,我們要為各個類建立專用的接口,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調用。本文例子中,將一個龐大的接口變更為3個專用的接口所采用的就是接口隔離原則。在程序設計中,依賴幾個專用的接口要比依賴一個綜合的接口更靈活。接口是設計時對外部設定的“契約”,通過分散定義多個接口,可以預防外來變更的擴散,提高系統的靈活性和可維護性。

說到這里,很多人會覺的接口隔離原則跟之前的單一職責原則很相似,其實不然。其一,單一職責原則原注重的是職責;而接口隔離原則注重對接口依賴的隔離。其二,單一職責原則主要是約束類,其次才是接口和方法,它針對的是程序中的實現和細節;而接口隔離原則主要約束接口接口,主要針對抽象,針對程序整體框架的構建。

采用接口隔離原則對接口進行約束時,要注意以下幾點:

  • 接口盡量小,但是要有限度。對接口進行細化可以提高程序設計靈活性是不掙的事實,但是如果過小,則會造成接口數量過多,使設計復雜化。所以一定要適度。

  • 為依賴接口的類定制服務,只暴露給調用的類它需要的方法,它不需要的方法則隱藏起來。只有專注地為一個模塊提供定制服務,才能建立最小的依賴關系。

  • 提高內聚,減少對外交互。使接口用最少的方法去完成最多的事情。

運用接口隔離原則,一定要適度,接口設計的過大或過小都不好。設計接口的時候,只有多花些時間去思考和籌划,才能准確地實踐這一原則。

 

5.迪米特法則(最少知道原則) (Demeter Principle)                                                                                  

為什么叫最少知道原則,就是說:一個實體應當盡量少的與其他實體之間發生相互作用,使得系統功能模塊相對獨立。也就是說一個軟件實體應當盡可能少的與其他實體發生相互作用。這樣,當一個模塊修改時,就會盡量少的影響其他的模塊,擴展會相對容易,這是對軟件實體之間通信的限制,它要求限制軟件實體之間通信的寬度和深度。

定義:一個對象應該對其他對象保持最少的了解。

迪米特法則的初衷是降低類之間的耦合,由於每個類都減少了不必要的依賴,因此的確可以降低耦合關系。但是凡事都有度,雖然可以避免與非直接的類通信,但是要通信,必然會通過一個“中介”來發生聯系,例如本例中,總公司就是通過分公司這個“中介”來與分公司的員工發生聯系的。過分的使用迪米特原則,會產生大量這樣的中介和傳遞類,導致系統復雜度變大。所以在采用迪米特法則時要反復權衡,既做到結構清晰,又要高內聚低耦合。

6.開閉原則(Open Close Principle)                                                                                               

開閉原則就是說對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,需要面向接口編程。

定義:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM