一文掌握設計模式(定義+UML類圖+應用)

2023-06-26 18:00:27

一、引子

  從學程式設計一開始就被告知,要想做一名優秀的程式設計師兩大必要技能:1.原始碼閱讀(JDK、C等底層語言封裝) 2.設計模式(使用某種語言優雅的落地典型場景功能)。一般隨著工作年限的增長,被迫對底層語言/框架原始碼閱讀的越來愈多,但是設計模式如不刻意去學習,永遠不會真正掌握。筆者把設計模式比喻成程式設計師的「絕世神功」,掌握了設計模式,對快速閱讀原始碼、優雅地編寫程式有極大的促進作用,可以說就像打通了任督二脈一樣。

1.1 設計模式由來

1995年,GoF(Gang of Four四人幫)合作出版了《設計模式:可複用物件導向軟體的基礎》(Design Patterns – Elements of Reusable Object-Oriented Software) 一書,書中總結了23種物件導向設計模式,也就是大名鼎鼎的GOF設計模式

 

1.2 23種 VS 24種

一直聽說有24種設計模式(網上一大堆標題24種設計模式,然後列舉的只有23種,也是無語),公認的GOF只有23種,還有一種是啥?大部分文章都說是簡單工廠模式(Simple Factory)。筆者搜遍全網(連chatGPT也問了),沒找到第24種設計模式誰提出的。這第24種設計模式,有說是簡單工廠模式,也有說是靜態工廠模式,也有說簡單工廠模式=靜態工廠模式,真是一塌糊塗,亂七八糟。神奇的是這些文章的作者都沒深究過這個問題,估計大都是拿來主義。

到底幾種?

---23種!!!GOF的23種設計模式即可還有2個模式:簡單工廠、靜態工廠,確實在後續應用比較多,但在1995年未列入GOF。

二、知識預備

想要練成絕世武功,內功心法和外家套路缺一不可。要想練成「設計模式」這一絕世武功,「物件導向設計原則」就是內功心法(遵循的設計原則),「UML統一建模語言」就是外家套路(程式碼落地建模工具)。練功之前,咱們先簡單學習下這2個必備的基礎技能。

2.1 物件導向設計原則

物件導向設計的目標之一就是支援可維護性複用,一方面需要實現設計方案的重用,另一方面要確保系統易於拓展和修改,具有較好的靈活性。7種物件導向設計原則,其中前5條是強約束性的建議都做到,後2條儘量做到即可。

1、單一職責原則(Single Responsibility Principle , SRP):一個物件應該只包含單一的職責,並且該職責被完整地封裝在一個類中。--單一職責原則是實現高內聚低耦合的指導方針。

理解:

  • 一個類只負責一個職責。

2、開閉原則(Open Close Principle):對擴充套件開放,對修改關閉。--開閉原則是物件導向設計的目標

理解:

  • 使用介面和抽象類。實現不修改原始碼,又可以拓展新方法。

3、里氏代換原則(Liskov Substitution Principle):所有參照基礎類別(父類別)的地方必須能透明地使用其子類的物件。--里氏代換原則是實現開閉原則的基礎

理解:

  • 把父類別設計為抽象類或者介面。
  • 子類必須實現父類別的所有方法。

4、依賴倒轉原則(Dependence Inversion Principle):高層模組不應該依賴低層模組,它們都應該依賴抽象。抽象不依賴於細節,細節應該依賴於抽象。--依賴倒轉原則就是物件導向設計的主要手段

理解:

  • 要針對介面程式設計,不要針對實現程式設計。
  • 依賴注入:將一個類的物件傳入另一個類,注入時儘量注入父類別物件,程式執行時通過子類物件覆蓋父類別物件。

5、介面隔離原則(Interface Segregation Principle):使用者端不應該依賴那些它不需要的介面(方法),--介面級的單一職責原則

理解:

  • 大介面要分割成小介面,介面專用。
  • 使用者端使用專用介面。

6、迪米特法則(Demeter Principle):即最少知道原則,一個實體應當儘量少的與其他實體之間發生相互作用,減少耦合

理解:

  • 依賴者只依賴該依賴的物件,被依賴者只暴露該暴露的物件。

7、合成複用原則(Composite Reuse Principle):儘量使用合成/聚合的方式,而不是使用繼承,降低耦合

理解:

  • 少用繼承,降低耦合。

2.2 UML建模

UML1.X有9個圖,UML2.0有12個圖,具體如下圖所示:

一般,我們掌握 UML1.X 常用的9種圖即可,分為兩類:

  • 靜態圖:用例圖、類圖、包圖、物件圖、部署圖
  • 動態圖:順序圖(時序圖),通訊圖(UML1.x 時稱為共同作業圖),狀態機圖,活動圖

本文需要使用靜態圖的一種:類圖。關注類圖中類之間的6種關係耦合度大小排序):泛化 = 實現 > 組合 > 聚合 > 關聯 > 依賴

2.2.1 類圖-關聯關係

 

  • 1.泛化:指的是繼承關係,表達一般和特殊。符號:空心三角箭頭的實線,箭頭指向父類別。(注:UML中只有泛化,繼承是開發角度的描述。)
  • 2.實現:指的是類與介面的關係,表達類實現了介面的特徵行為。符號:帶三角箭頭的虛線,箭頭指向介面。
  • 3.組合:指的是整體與部分的關係, 但部分不能離開整體而單獨存在。符號:帶實心菱形的實線,菱形指向整體。
  • 4.聚合:指的是整體和部分關係,且部分可以離開整體而單獨存。符號:空心菱形的實心線,菱形指向整體。
  • 5.關聯:指的是類和類的關係,表達一個類知道另一個類的屬性和方法。符號:帶普通箭頭(或實心三角形箭頭)的實心線。
  • 6.依賴:指的是使用的關係, 即一個類的實現需要另一個類的協助, 所以要儘量不使用雙向的互相依賴。符號:帶箭頭的虛線,指向被依賴的類。

2.2.2 類圖-表示方法

  • 從上到下分為3部分:類名、屬性、方法。
  • 符號含義:+ public、- private、#protected

三、設計模式

本節列舉24種設計模式,其中建立型模式6種、結構型模式7種、行為型模式11種。每個模式從3個角度進行分析:定義、UML類圖、應用

3.1 建立型模式

建立型模式,就是用來建立物件的。包含3塊:單例/原型模式、工廠模式(簡單工廠、工廠方法、抽象工廠)、建造者模式。

1.單例模式(Singleton)

定義:保證一個類僅有一個範例,並提供一個存取它的全域性存取點。

UML類圖:

 

應用:待補充

 

2.原型模式(Prototype)

定義:用原型範例指定建立物件的種類,並且通過拷貝這個原型來建立新的物件。

UML類圖:

 

3.簡單工廠模式(Static Factory Method Pattern)

定義:定義一個工廠類,使用static方法建立物件。根據不同引數返回不同範例,每增加一個物件需要修改工廠類,違背了「開閉原則」。--不推薦使用。

UML類圖:

應用:

  • Calendar 類獲取日曆類物件,static getInstance方法,根據引數獲取一個Calendar子類物件。
  • Logback 中的 LoggerFactory紀錄檔工廠 ,static getLogger方法根據name/Class獲取 Logger 物件。
  • Spring的BeanFactory介面,getBean(),根據name/Class獲取不同物件,但不是static修飾的,嚴格來說不算簡單工廠。

4.工廠方法模式(Factory Method)

定義:定義一個用於建立物件的介面,讓子類決定將哪一個類範例化。此模式使一個類的範例化延遲到其子類

UML類圖:

應用:

  • java.net.URLStreamHandlerFactory作為工廠方法介面,定義了抽象方法createURLStreamHandler建立產品URLStreamHandler。工廠方法實現類為sun.misc.Launcher內部類Factory。

5.抽象工廠模式(Abstract Factory)

定義:提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。用於建立一個產品族的產品。

UML類圖:

 

應用:

  • java.sql.Connection介面就是抽象工廠介面,定義了建立Statement產品族:Statement createStatement()、PreparedStatement prepareStatement(String sql)、CallableStatement prepareCall(String sql) 。PgConnection impl BaseConnection(extends Connection)就是一個具體工廠,專用於PostgreSql資料庫的連線工廠。PgConnection複寫了Statement產品族(3個建立)方法。

6.建造者模式(Builder)

定義:將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。

UML類圖:

 

 應用:

  • 實際應用中,具體建造者Builder一般直接使用靜態內部類實現。建立產品時類似:XXX.Builder(引數1,引數2,...).build()。Mybatis原始碼org.apache.ibatis.builder包下:MapperBuilderAssistant指揮者中,產品有:ParameterMap、ParameterMapping、ResultMap、、ResultMapping、Discriminator、MappedStatement等。每個產品都有自己的建造者靜態內部類Builder(定義了build())。

3.2 結構型模式

1.介面卡模式(Adapter)

定義:將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。

UML類圖:

應用:

Spring AOP中,org.springframework.aop.framework.adapter包下的AdvisorAdapter介面,有3個介面卡實現類:AfterReturningAdviceAdapter、MethodBeforeAdviceAdapter、ThrowsAdviceAdapter把每一個Advisor中的Advice都要適配成對應的MethodInterceptor物件。這裡使用的是方式二物件介面卡的變種。比如MethodBeforeAdviceAdapter介面卡通過getInterceptor()獲取到MethodBeforeAdviceInterceptor,最終結合攔截器模式(非GOF23),呼叫advice的advice.before()實現。

  • 目標介面:AdvisorAdapter介面
  • 適配者:MethodBeforeAdvice介面
  • 介面卡:MethodBeforeAdviceAdapter(advisor->advice)+MethodBeforeAdviceInterceptor(把適配者advice做入參構造,最終呼叫適配者方法執行)

比較複雜,圖示如下:

 

2.橋接模式(Bridge)

定義:將抽象部分與它的實現部分分離,使它們都可以獨立地變化。

UML類圖:

拆出一個維度出來,進行聚合,可有效減少子類的個數。原本維度1個數*維度2個數->維度1個數的子類+維度2個數的聚合實現+1個聚合介面

應用:

java.sql包下的DriverConnection介面,通過DriverManager進行橋接(不像圖中的聚合實現)。DriverManager定義了registerDriver和getConnection方法。registerDriver可以註冊各種驅動。Connection介面可實現連線多種資料庫。2個維度分開進行拓展實現,進行了脫耦。

3.組合模式(Composite)

定義:將物件組合成樹形結構以表示「部分-整體」的層次結構。它使得客戶對單個物件和複合物件的使用具有一致性。

UML類圖:

組合模式分為透明式(樹枝特有方法在抽象類中體現,樹葉類複寫空實現,使用者端使用時無需區分樹枝還是樹葉)和安全式(樹枝特有方法在樹枝類中體現)。當節點具有一致行為時採用透明式,當各節點行為不一致較多或樹枝節點較為健壯時採用安全式。

應用:

  • Map介面作為Component容器介面HashMap作為Composite樹枝、Node實現了Map介面下的唯一介面Entry<K,V>kv對映實體介面作為Leaf樹葉,HashMap有一個屬性Node<K,V>[] table連結串列首歌節點組成的陣列,可理解為聚合了Map介面。HashMap複寫putAll(Map<? extends K, ? extends V> m),可理解為樹枝的特有方法(Node沒有),很明顯這是安全式組合模式。

4.裝飾器模式(Decorator)

定義:動態地給一個物件新增一些額外的職責。就擴充套件功能而言, 它比生成子類方式更為靈活。

UML類圖:

 

應用:待補充

5.外觀模式(Facade)

定義:為子系統中的一組介面提供一個一致的介面,Facade模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。

UML類圖:

應用:待補充

6.享元模式(Flyweight)

定義:運用共用技術有效地支援大量細粒度的物件。

UML類圖:

應用:待補充

7.代理模式(Proxy)

定義:為其他物件提供一個代理以控制對這個物件的存取。

UML類圖:

應用:待補充

3.3 行為型模式

1.責任鏈模式(Chain of Responsibility)

定義:解除請求的傳送者和接收者之間耦合,而使多個物件都有機會處理這個請求。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它。

UML類圖:

應用:待補充

2.命令模式(Command)

定義:將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求紀錄檔,以及支援可取消的操作。

UML類圖:

應用:待補充

3.直譯器模式(Interpreter)

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

UML類圖:

應用:待補充

4.迭代器模式(Iterator)

定義:提供一種方法順序存取一個聚合物件中各個元素,而又不需暴露該物件的內部表示。

UML類圖:

應用:待補充

5.中介者模式(Mediator)

定義:用一箇中介物件來封裝一系列的物件互動。中介者使各物件不需要顯式地相互參照,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。

UML類圖:

應用:待補充

6.備忘錄模式(Memento)

定義:在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可將該物件恢復到儲存的狀態。

UML類圖:

應用:待補充

7.觀察者模式(Observer)

定義:定義物件間的一種一對多的依賴關係,以便當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並自動重新整理。

UML類圖:

應用:觀察者模式

8.狀態模式(State)

定義:允許一個物件在其內部狀態改變時改變它的行為。物件看起來似乎修改了它所屬的類。

UML類圖:

應用:待補充

9.策略模式(Strategy)

定義:定義一系列的演演算法,把它們一個個封裝起來, 並且使它們可相互替換。本模式使得演演算法的變化可獨立於使用它的客戶。

UML類圖:

應用:待補充

10.模板方法模式(Template Method)

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

UML類圖:

應用:待補充

11.存取者模式(Visitor)

定義:表示一個作用於某物件結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。

UML類圖:

應用:待補充

===參考===================

設計模式總結

設計模式與23種設計模式的簡單介紹

淺談UML中常用的9種圖