UML建模語言、設計原則、設計模式

2022-11-06 06:03:28

1、UML統一建模語言

定義:用於軟體系統設計與分析的語言工具

目的:幫助開發人員更好的梳理邏輯、思路
學習地址:UML概述_w3cschool

官網:https://www.omg.org/spec/UML


1.1、UML組成結構

flowchart TD; UML圖 --> 結構圖 & 行為圖; 結構圖 --> 剖面圖 & 包圖 & 複合結構 & 物件圖 & 類圖 & 元件圖 & 部署圖 行為圖 --> 互動圖 & 活動圖 & 狀態圖 & 用例圖 互動圖 --> 互動概圖 & 時序圖 & 順序圖 & 通訊圖

1.2、各類UML圖範例


1.2.1、用例圖

定義:用來顯示一組用例、參與者以及它們之間關係的圖

是從需求分析出發,不考慮程式碼實現的事,描述使用者希望如何使用一個系統。通過用例圖可以知道誰是系統相關的使用者,他們希望系統提供哪些服務,以及他們需要為系統提供什麼樣的服務


1.2.1.1、用例圖組成

名稱 含義 圖例
參與者
(Actor)
也叫角色,表示系統的使用者(在系統之外,但與系統直接互動的物件)
注:這裡的使用者並不一定是指人,如:做的是公共API介面,那API的呼叫者就是使用者
用例
(Use Case)
描述參與者可以感受到的系統服務或者功能(換言之:描述系統為了實現使用者的目標而執行的一個系統功能單元)

注:用例的目標是要定義系統的一個行為,但並不顯示系統的內部結構 / 某個功能的具體實現
系統邊界 也叫容器(但這個名字詞不達意),系統與系統之間的界限 兩種都對,但最常用的是矩形
子系統
(SubSystem)
一堆用例的集合,這堆用例之間有著緊密關係(換言之:展示系統的一部分功能)

1.2.1.2、用例圖之間的關係

符號 名稱 說明 圖示
—————— 關聯 參與者與用例之間的通訊(參與者 和 用例之間的關係)
--------> 包含 提取公共互動,提高複用
換言之:一個用例需要某種功能,而該功能被另外一個用例定義,那麼在用例的執行過程中,就可以呼叫已經定義好的用例(用例 與 用例之間的關係)

箭頭指向:指向分解出來的功能用例
擴充套件 基用例保持不動,動態擴充套件基用例的功能(用例 與 用例之間的關係)
擴充套件關係的限制規則(也是區別包含關係的手段):將一些常規的動作放在一個基本用例中,將可選的或只在特定條件下才執行的動作放在它的擴充套件用例中

箭頭指向:指向基用例
表示方式使用表中左邊說的那種符號或者下圖這種和包含一樣,構造型換一下也行(通常用的是下面這種)
泛化 / 繼承 子用例中的特殊行為都可以作為父用例中的備選流存在(用例 與 用例之間的關係[父子用例] )

箭頭指向:指向父用例(箭頭實心和空心都可以,嚴格來講是空心)
<<include>> 構造型 就是擴充套件的意思(UML中通用的擴充套件表現形式),相當於說明

include 是包含關係關鍵字
extend 是擴充套件關係關鍵字

用例圖範例展示

  • 注:下圖 出鈔 和 憑條與退卡 是說的物理機ATM保險櫃的功能,並不是說出鈔 和 憑條與退卡 是客戶從提款機中看到的這二者功能( PS:ATM分為兩部分,一部分是我們所做的軟體系統,即:下圖的ATM系統,另一部分是ATM保險櫃[錢真正在的地方],可以說就是硬體,自動取款只是通過我們編寫的軟體系統去操作了保險櫃,從而把錢吐出來)


1.2.2、活動圖

活動圖本質是流程圖,從流程圖演變而來的。


定義:對系統的動態行為建模的一種工具,描述的是活動的順序,即:從一種活動到另一種活動的控制流(本質:活動之間控制權的轉換)


對於上述我所謂的活動之間控制權的轉換的說明,如:我去進行核酸檢測(下圖不嚴謹,當流程圖來看,只是混入了活動圖的圖示在裡面)

  • 通過下圖可以推論出:控制權不會丟失,可以分散 / 分支、最後也會合併,不會消失,只是從一個活動到了另一個活動「手裡」而已(像能量守恆一樣)


1.2.2.1、活動圖的組成

1.2.2.1.1、基本組成
名稱 定義 符號
開始狀態 表示活動開始的一個狀態

注:開始狀態只能有一個
下面兩種表示方式都可以
結束狀態 表示活動結束的一個狀態

注:結束狀態可以有多個
下面兩種都行
活動 / 動作 很多動作的集合
一個動作就是一個步驟

如:打籃球就是一個活動,但是:裡面卻可以有很多動作,譬如:分組、進攻、防守......當然這些還可以再細分

另外:動作其實就是子圖(即:一個活動的內部邏輯。後續會說明)
狀態 和活動等價
特別點:嚴格來講狀態只分為開始狀態和結束狀態,活動符號並沒有上面那種表示法(新版。舊版有),現在官網中對活動符號的表示如下:
注意:和活動符號的圖不太一樣,當然:用哪一個都可以
控制流 就是控制權的流動方向,也有人叫「轉移」 下面兩種表示方式都可以
物件 某個類的範例或者是某些活動輸出的結果(可以理解為是一個引數,某個活動狀態需要藉助某個引數,藉助的這個引數就是一個物件)
在整個活動圖中一個物件是可以多次出現的(類的範例嘛)
注意物件名稱下面是有下劃線的

另外:物件名稱注意用名詞來進行命名
物件流 可以理解為資料流
就是活動與物件之間的資料傳遞,也就是活動之間需要某個 / 某些物件來參與,那麼:控制流就變成了物件流
下面二者都可以
流終止 表示控制流 / 物件流的結束
這個其實可以不要,終止了不在圖中表示出來不就表示終止了嗎
事件 可以理解為訊號
分為發出訊號 和 接收訊號
下圖的中間兩個,左為發出訊號,右為接收訊號

邏輯:
處理訂單後,發出請求付款的訊號
活動等待接收確認付款的訊號
活動接收到了付款訊號之後,即發貨

還有一種事件:叫時間事件(也可以當做是一個活動)
就是等待某一個時間才能觸發某個活動
時間名稱放在符號下方
判定活動 就是流程圖中的邏輯判斷 注意:這個不是分支或者合併,還差一點東西才能變成分支 或 合併(就是幾根控制流的線),有了這一步才能說建立分支
同步條 就是控制流(控制權)的控制
下面看到了分叉與會合之後就一清二楚了
分為水平同步 和 垂直同步(二者沒區別,是畫圖的方向問題,看畫的圖箭頭方向是怎樣的,然後選擇對應的同步條即可)

分支與合併(都需要判定活動參與)

  • 分支:可以理解為控制權的分散(一個活動的控制權分給了多個活動),要求:必須是一個控制輸入流、兩個及以上的控制輸出流,符號表示方式如下(菱形+四個控制流箭頭):
    • 注意:判定活動(即:菱形)不是分支,判定活動+控制流才是分支
  • 合併:可以理解為控制權的融合(多個活動的控制權給到了一個活動),就是分支的逆向。要求:多個控制輸入流、一個控制輸出流,符號表示如下:

分叉與會合(都需要同步條參與)

  • 分叉:用於將一個控制流分為兩個或多個並行執行的分支,要求:必須是一個控制輸入流、兩個及以上的控制輸出流,符號表示如下:
  • 會合:用於將兩個或多個控制流合併到一起形成一個單向的控制流,要求:多個控制輸入流、一個控制輸出流,符號表示如下:

泳道

  • 定義:表明每個活動是由哪些物件負責完成的(換言之:表示活動的發起者是誰,物件不一定非要是人,可以是系統、會員........),也可以說是:一個物件進行了哪些活動。當然:可以換個名字就更好理解了,即:分割區(一個區域中有哪些活動狀態)
  • 泳道分類:水平泳道和垂直泳道,和前面的同步條是一樣的,水平和垂直沒什麼區別,也是畫圖方向的問題,符號表示如下:
    • 範例:

子圖 / 子活動圖

  • 在前面的表格中提到過,就是動作(活動是動作的集合體,類似Java中的物件 ---抽象---->類,很多動作 ------抽象------>活動),可以理解為:是對某個活動畫的補充圖,只不過這個補充圖是較為詳細的邏輯表現(類似一個活動需要引入的粗糙點的流程圖)
  • 定義:對某個活動進行的續圖說明,符號表示就是一個倒著的「掃把」(下圖這種顏色的圖是我在官網下載的檔案中嫖的,版本是2.5.1)
      • 左邊活動中有一個倒著的掃把就表示這個活動要引入一個子圖,而右邊就是引入的子圖內容
  • 注意:動作和活動這兩個不能說完全等價(鑽字眼兒),用上圖舉例:
    • 如果左邊的活動裡面的一部分流程描述 / 活動組成內容剛好在另一個真正的活動圖中分毫不差地體現了,而左邊這個活動需要引入,那麼此時就可以說子圖就是活動,即:動作等價於活動
    • 如果左邊的活動裡面需要的部分流程描述 / 活動組成內容沒有找到其他活動圖來完全貼合其描述,那麼就是需要新畫一個子圖來對左邊的活動進行簡略描述,繼而在左邊活動中引入,則:此時子圖是動作,而不是真正的活動,即:動作不等價於活動

擴充套件區域 / 擴充區 / 擴充套件區

  • 定義:將一個需要體現在活動圖中的迴圈過程進行提取(不需要體現在活動圖中的,可以直接使用活動節點來略寫),有點類似於子圖,但是擴充套件區的關鍵就是提取的是一個活動中的迴圈過程,但不是把迴圈過程重新弄成一個活動圖,而是就在當前活動圖中
  • 符號表示如下(左為簡單寫法,右為完整寫法):
  • 範例:

1.2.3、類圖、物件圖

定義(人話):就是表示一個類 / 介面的組成結構

​ 對於屬性:看修飾符是什麼(public、private、static等)、資料型別是什麼、屬性名叫什麼、是否有預設值

​ 對於方法:看修飾符是什麼(public、private、static等)、返回值是什麼、方法名是什麼、引數型別和名字是什麼

​ ..................


關鍵字 表示方式
public + 表示
private - 表示
protected # 表示
package ~ 表示
abstract *表示
static 用 $ 表示
泛型 ~泛型型別~表示 如:List~int~ position
註解 以<<開頭 註解內容 以>>結尾

可以用一個特定的標記文字來註釋,如:
<<Interface>> 代表一個介面
<<abstract>> 代表一個抽象類
<<Service>> 代表一個服務類
<<enumeration>> 代表一個列舉
註釋 %%註釋內容 表示
註釋開始到下一個換行符之前的任何文字都將被視為註釋,包括任何類圖語法
這是對類圖進行註釋,即:說明,不是說屬性、方法.....都搞這個

對於類:以 https://www.processon.com 網址中的為例(下列名字見名知意,對照上面的人話定義即可)


對於介面:和上面的類圖是相通的


1.2.3.1、類圖之間的關係

名字 指向 範例 圖示
泛化 / 繼承 子類 指向 父類別
子抽象類 指向 父抽象類
學生類 繼承 人類 實心三角箭頭 和 空心三角箭頭都行
組合 菱形部分指向整體
是屬於包含關係中的一種(組合、聚合、關聯)
A has - a B的關係。一句話:一榮俱榮、一毀俱毀
整體和部分關係、整體部分不可分離、比聚合更強
如:一個類中的一個屬性為private Head head = new Head(); ,直接繫結在一起的
大雁和大雁翅膀的關係,兩者是同生共死的
聚合 菱形部分指向整體[箭頭指向個體,這個箭頭可有可無]
是屬於包含關係中的一種(組合、聚合、關聯)
A has - a B的關係。
還是整體和部分的關係,但是建立時有可能是分隔開的
如:一個類中的屬性為private IDCard card;這個屬性值可能會後續在其他地方傳進來(有參構造)
大雁和雁群之間的關係
更如:電腦和主機板
關聯 / 關聯類 箭頭指向成員變數類(單關聯)。這種單關聯注意一種模型圖(最嚴格的一種寫法):在沒有箭頭的一方可能會有一個「×」,這表示:箭頭的一方一定沒有關聯「×」的一方。反之:沒有「×」表示當前模型中沒有明確說明無箭頭一方是否關聯有箭頭一方

箭頭指向彼此(雙關聯),如:A類中有B類作為成員變數(屬性),B類中有A類作為屬性,此時彼此都產生關聯,即為雙向箭頭 / 雙關聯

還有一種寫法:兩邊都沒有箭頭,就是一根實線,這種直接說是包含關係(是一種不嚴謹的寫法),這種直接當做屬性,A類和B類至於是單關聯還是雙關聯都行


注意一種情況:下面這種 下圖不等價於上圖(它們表示「不同組」,即:上圖表示Car關聯的是駕駛這輛Car的Person,而Person駕駛的是同一輛Car;而下圖表示Car關聯的是駕駛這輛Car的Person,但Person關聯 / 駕駛的是另外的Car[某輛車需要人類中的某個人來駕駛,而人類中的另外某個人可以駕駛另外型號的車])

關聯關係是屬於包含關係中的一種(組合、聚合、關聯)
A has - a B的關係
是整體和部分的關係,可以分割,是後來組合在一起的
換言之:兩個類之間的關聯,也可以是一個類和自身的關聯
班級類和學生類,學生類作為成員變數存在於班級類中
也如:人有汽車、人有電腦
類關聯 這就是比關聯類更細節性的畫圖(讓某些情景不產生歧義)
抽象了多對多的本身(A集合和B集合各自全集的笛卡爾乘積的子集)
下圖為關聯類

上圖表達的邏輯:1、一個人有多條出席會議記錄,對應的是一個會議[圖符合,因為這個人可能會和不同的人開同一個會議];2、一個會議有2或以上條出席會議記錄,對應的是一個人[圖也符合,因為這個會議某個人可能參加了多次]。所以雖然圖看起來沒問題,但是不貼合現實[一個人出席了同一個會議那記錄為1就可以了,按上圖來看就會出現某個人有多條出席會議記錄 - 即:重複了],所以上圖中「總的出席會議記錄數量大小」在數學上應該是:多人、多會議各自全集的笛卡爾乘積的子集[會排除重複的],因此為了貼合現實圖就改造為如下的類關聯

邏輯:1、2或以上的人對應多個會議(多對多);2、出席會議記錄類關聯了人和會議兩個類,並且出席會議記錄最後的結果為兩邊集合各自的全集的笛卡爾乘積的子集[排除重複的結果])
依賴 箭頭指向入參類
A need - a B 的關係
班級類和學生類,班級類作為學生類的方法入參
實現 箭頭指向介面 學生類實現人類

1.2.3.1.1、各類圖關係範例
1.2.3.1.1.1、泛化 / 繼承
public class DaoSupport{

    private String name;

    public void save(Object entity){}

    public void delete(Object id){} 
}


public class PersonServiceBean extends Daosupport{
}


使用typora的mermaid指令碼畫圖

語法:
<|-- 表示繼承 箭頭指向的一方是被繼承者

+ 表示 public

- 表示 private

語法學習地址Markdown教學-慕課網 (imooc.com)
mermaid語法學習地址https://mermaid-js.github.io/mermaid/


```mermaid

    classDiagram
    %% 要用註釋只能放在這裡,對類圖進行說明
        class DaoSupport{
			屬性返回值型別放在前面 -String的-和String中間最好也別搞空格(只是這個mermaid指令碼中而已),但是嚴格寫法應該是 -name : String
			-String name
			方法返回值型別是在後面 另外:+void的+和void之間別搞空格
            +save(Object entity) void
            +delete(Object id) void
        }

        class PersonServiceBean{

        }

        DaoSupport <|-- PersonServiceBean
```


效果如下:

classDiagram %% 這是一個小試牛刀的類圖 class DaoSupport{ -String name +save(Object entity) String +delete(Object id) void } class PersonServiceBean{ } DaoSupport <|-- PersonServiceBean

1.2.3.1.1.2、組合

public class Person {

    // 組合關係:某個類的物件 當做 當前類的屬性,並已經new了
    private Head head = new Head();
}

mermaid指令碼畫圖語法:

*-- 表示組合,星* 指向的是整體 即:菱形指向整體

```mermaid

    classDiagram

        class Person{

        }

        Person *-- Head  // 表示的是:Person 組合 Head
```

效果如下:

classDiagram class Person{ } Person *-- Head

1.2.3.1.1.3、聚合
public class Person {
    // 聚合關係
    private IDCard card;
    
    // 對照:組合關係
    private Head head = new Head();
}
```mermaid

classDiagram
	class Person{
	
	}
	
	Person o-- IDCard  // 這是字母o 不是0,菱形指向整體 即:Person聚合了IDCard

```

效果如下:

classDiagram class Person{ } Person o-- IDCard

1.2.3.1.1.4、關聯、依賴、實現


1.2.3.2、物件圖

定義:表示在某時刻物件和物件之間的關係(由於物件存在生命週期,因此物件圖只能在系統某一時間段存在)
物件圖是類圖的範例,幾乎使用與類圖完全相同的標識。一個物件圖可看成一個類圖的特殊用例


1.2.4、順序圖(時序圖 / 序列圖)和通訊圖

1.2.4.1、順序圖

定義:用來表達物件間訊息傳遞的順序

一般來說:順序圖也叫時序圖、序列圖(這三個在英文中都是Sequence ),但是:嚴格來說(電子通訊方面),順序圖是順序圖,時序圖 / 序列圖是時序圖 / 序列圖(在電子通訊方面,這個實在要對應的話,就對應UML中的時間圖Timing Diagram),在電子通訊領域這二者要表達的意思並不一樣,但是對於我們程式設計這一行業來說:直接把順序圖、時序圖、序列圖等價也沒錯,叫其中哪一個名字都無所謂


1.2.4.2、順序圖組成


名稱 說明 圖示
參與者 也叫角色,表示系統的使用者(在系統之外,但與系統直接互動的物件)
注:這裡的使用者並不一定是指人,如:做的是公共API介面,那API的呼叫者就是使用者
物件 就是物件圖中的物件,可理解成某個類的範例
如果只顯示類名,則:去掉上圖中「物件名」即可,即: :某個類型別
如果只顯示物件名而不顯示類名,則:去掉 : 及之後的即可,即: 物件名
生命線 表示物件的生存時間(就是一條向下的虛線)
啟用 表示某種行為的開始或結束,就是一個小矩形
反之:沒有小矩形的那些虛線就是物件的休眠
訊息 分為同步訊息 和 非同步訊息
在UML中,指的是:物件與物件之間的通訊
在順序圖中是用 兩個物件之間帶箭頭的線來表示
注:下圖真實含義是另一個,拆開看,單獨只看兩個帶箭頭的線即可,整個圖的場景是另一個意思

帶實心箭頭的實線:傳送訊息 / 方法呼叫
帶開放式的三角箭頭的虛線:返回訊息 / 返回值(特別需要返回訊息時就用,不特別需要的話,那麼採用下面同步那種簡化畫法就可以了)
同步訊息 就是訊息傳送完畢了,就返回訊息(一條龍服務)
當然:這也意味著阻塞和等待

當然:上圖也可以換成前面那種用「開放式的三角箭頭的虛線返回訊息」,上圖這種是簡寫形式
注意:1、圖中引數哪裡要是沒有,不表示就沒有訊息的傳送(呼叫方法,方法本身就是訊息傳送);2、如果沒有返回值,即:void那也不表示沒有返回訊息(當進行方法呼叫時,右邊已經啟用,一樣會進行阻塞,即:右邊啟用條做完該做的事情,照樣給左邊返回資訊,告知呼叫者事情做完了,只是圖中不會顯示地畫出來而已,但內部邏輯還是有的)
非同步訊息 和同步換一下,就是訊息傳送完畢了,返回訊息可以後面給(中間可以做完另外的事情再給)
同理:這就意味著非阻塞


注意:和同步訊息畫法不一樣(箭頭不一樣),另外:非同步中返回訊息不是虛線,是實線(就是變成右邊物件向左邊物件傳送訊息:內容就是左邊物件要的返回訊息 / 返回值)
持續訊息時間 字面意思
出現的情況:有些訊息需要持續很長一段時間,從而需要標註出來(如:大檔案上傳)
第一種:使用帶有實心箭頭的傾斜的線表示(下圖 {} 中括號中是條件控制,在後續會介紹)

第二種:表達準確的時間(在第一種的基礎上,繼續加入東西)

上圖表示:在2h內上傳檔案,然後返回結果之後等待5min以上,檢查上傳情況
重入訊息 A物件給B物件發訊息,在B還未返回訊息之前,B給A發了一條訊息
自我呼叫 是重入訊息的特例(A給B發訊息,在B未返回之前,A又給自己發了一條訊息)
所以就是自己玩自己(俗稱:自wei)
下面兩種畫法都可以(嚴格來講是第一種)
無觸發和無接收訊息 上面那些都是基於系統本身內部的,但是:有些可能需要使用到系統外部的某些東西(物件、參與者....)
在技術實施開發層面一般不會見到,其他崗位會有
物件的建立 字面意思 被建立物件會比生命線矮一截(就是下圖中右邊比左邊矮一點)
物件的銷燬 字面意思,表示方式就是在物件銷燬時打一個「×」

1.2.4.3、執行控制

關鍵字 說明
alt 備用多個片段:只執行條件為真的片段(就是條件分支if else
opt 可選項:僅當提供的條件為真時才執行片段。相當於只有一條痕跡線的alt
par 並行:每個片段並行執行
loop 迴圈:片段可以執行多次,並且防護指示迭代的基礎
region 關鍵區域:片段只能有一個執行緒一次執行它
neg 否定:片段顯示無效的互動
ref 參考:指在另一個圖中定義的互動,繪製框架以覆蓋互動中涉及的生命線(可以定義引數和返回值)
sd 序列圖:用於包圍整個序列圖

上面的都是官方話,接下來舉一些常用的例子。


1.2.4.3.1、條件分支 alt


1.2.4.3.2、可選項 opt
  • 包含一個可能發生或不發生的序列

  • 只要當我成績score小於60時,老媽打我這件事情肯定會發生。大於就不會發生

1.2.4.3.3、迴圈 loop
  • 圖是網上偷的,看懂了分支,那回圈也能很容易看懂了


1.2.4.3.4、並行 par


1.2.4.4、順序圖範例

  • SpringMVC執行流程原理

  • 轉化為順序圖


1.2.4.5、通訊圖

  • 通訊圖和順序圖可以等價互轉

  • 通訊圖犧牲了順序上的直觀性,增強了佈局和關聯上的直觀性;而順序圖是相反的

  • 要搞邏輯就看序號(1:1.1:2:2.1:..........)

  • 假如一個序列圖如下:

  • 轉化為通訊圖(兩張圖對照看就懂了):


1.2.5、狀態圖 / 狀態機圖 / 轉移圖

定義:狀態圖又名狀態機圖或轉移圖,指的是:一個特定物件的所有可能的狀態以及引起狀態轉換的事件
一個物件必然會經歷一個從開始建立到最終消亡的完整過程,這稱之為物件的生命週期。物件在其生命週期內是不可能完全孤立的,它必然會接受訊息來改變自身,或者傳送訊息來影響其他物件。而狀態機就是用於說明物件在其生命週期中響應時間所經歷的狀態序列以及其對這些事件的響應。在狀態機的語境中,一個事件就是一次激發的產生,每個激發都可以觸發一個狀態轉換


1.2.5.1、狀態圖的組成

  • 一份簡單的狀態圖

狀態圖組成

名稱 說明
狀態 指的是物件在其生命週期中的一種狀況,處於某個特定狀態中的物件必然會滿足某些條件、執行某些動作或者是等待某些事件
動作 指的是狀態機中可以執行的哪些原子操作
原子操作:指的是他們在執行的過程中不能被其他訊息中斷,必須一直執行下去,以至最終導致狀態的變更或者返回一個值
事件 指的是發生在時間和空間上對狀態機來講有意義的那些事情
事件通常會引起狀態的變遷,促使狀態機從一種狀態切換到另一種狀態
活動 指的是狀態機中做的那些非原子操作
轉移 指的是兩個不同狀態之間的一種關係,表明物件在第一個狀態中執行一定的動作,並且在滿足某個特定條件下由某個事件觸發進入第二個狀態

1.2.5.2、狀態圖的五要素

注意:

  • 上圖中「觸發事件」和「監護條件」要同時生效的,即:只有「觸發事件」滿足「監護條件」,才能執行「動作」,從而讓第一個狀態轉移為第二個狀態
    • 「監護條件」寫法就是[監護條件][]不能丟
  • 上圖中整個「帶箭頭的三角的實線(單純地這根線)」就是「轉移」,或者稱之為:行為狀態也行
  • 上圖中整個「帶箭頭的三角的實線+上面的觸發事件[監護條件]/動作」就是事件,其中:觸發事件[監護條件]/動作 三者少哪一個都行,甚至全沒有也行

1.2.5.3、狀態圖中的狀態

注:初態和終態都不是真正的狀態,而是:偽狀態

名字 說明 圖示
初態 一個狀態圖有且只有一個初態 黑色實心
終態 一個狀態圖中可以有一個或多個終態,也可以沒有終態 一對同心圓(內圓為實心圓)
中間態 用圓角矩形表示,可以用一條水平橫線把它分成上、下兩部分。(面部分為狀態的名稱[必有]面部分是活動表[可選]

有些軟體是分成了三部分:面部分為狀態的名稱(必有)間部分為狀態變數的名字和值(可選)面部分是活動表(可選)
子狀態 就是狀態中套狀態
歷史狀態 就是一個物件曾經已經發生過的狀態的記錄,類似歷史紀錄檔
作用:用來恢復狀態的。如:斷電了,導致系統整個狀態結束了,恢復電之後想要回到斷電時的狀態就可以用
可以多層巢狀,就是一個套一個(有可能要恢復的狀態在很多層裡面)

舉例:

對於上面表格的中間態中說的活動表的說明:

  • 活動表又名轉換域

  • 表示式語法為:事件名(參數列)/動作表示式,其中:參數列也可以沒有,如下:

  • 在活動表中經常使用的3種標準事件

    • do 上圖已經見過了,指的是:在該狀態下的動作
    • entry 進入該狀態的動作
    • exit 退出該狀態的動作

1.2.6、元件圖 / 構件圖

定義:用來描述一個系統 / 功能的物理構件( 元件與元件之間的關係 )。包括檔案,可執行檔案,庫等。換言之:構成系統某一特定方面的實現結構
元件圖 = 構件 / 元件(Component)+介面(Interface)+關係(Relationship)+埠(Port)+聯結器(Connector)


1.2.6.1、元件圖的組成

1.2.6.1.1、元件

定義:是一個封裝好的物理實現單元,隱藏內部的實現,對外提供了一組介面
具有自己的身份標示和定義明確的介面。由於它對介面的實現過程與外部元素獨立,所以元件具有可替換性

人話:元件就是一個實際的檔案或者多個檔案組成的可執行程式(通俗的話來說[嚴格來講不能這麼理解,但是為了理解而理解,可以用]:元件就相當於Java的抽象和封裝思想(當然:懂Vue的話,那就懂元件化開發了,那就更不用解釋了)


元件的種類:

  • 原始碼元件:一個原始碼檔案或者與一個包對應的若干個原始碼檔案
  • 二進位制元件:一個目標碼檔案,一個靜態的或者動態的庫檔案
  • 可執行元件:在一臺處理器上可執行的一個可執行的程式單位,即所謂的可執行程式

元件長什麼樣子(UML1.x的畫法)


1.2.6.1.2、元件盒(就是元件)

定義:就是一個用來裝元件的盒子

當然:元件盒其實就是元件,這二者就是等價的,因為這盒子裡面裝的就是元件,因此:UML2.x中,元件就是元件盒

元件盒長什麼樣子


因此:元件的畫法就可以弄成下面幾種了

  • 矩形+圖示
  • 矩形+構造型標籤,就是上面元件盒的畫法,下圖構造型標籤<<>>中寫元件中文名字也行(但:建議用英文關鍵字)
  • 前面兩者都有的畫法,這種畫法構造型標籤<<component>>就只起到標識作用

1.2.6.1.3、介面

分為兩類:提供介面 和 需求介面

提供介面:又被稱為匯出介面或供給介面,由提供操作的元件提供,是元件為其他元件提供服務的操作的集合(如:商品元件提供商品相關的一堆介面)
需求介面:又被稱為引入介面,是元件向其他元件請求相應服務時的介面(訂單元件需要呼叫商品元件提供的介面)


提供介面長什麼樣子?

需求介面長什麼樣子?


1.2.6.1.4、埠
  • 這個已經在熟悉不過了
  • 就是一個被封裝的元件的對外視窗
  • 在封裝的元件中,所有出入元件的互動都要通過埠。它是被封裝的元件與外界的互動點,遵循指定介面的元件通過它來收發訊息
  • 表示方式:就是一個小矩形

1.2.6.1.5、聯結器
  • 指的就是元件間的連線,換言之:就是元件之間的關係,也就是在類圖中的實線、泛化關係........等等,所以:聯結器不只是在元件圖中,在UML圖中都有,就是那些線嘛(在元件圖中,這種關係有個專業名詞叫:組裝聯結器,還有一個委託聯結器:連線外部介面的埠和內部介面[這個不需要多瞭解])
    • 實現關係:用直線表示
    • 依賴關係:用帶箭頭的虛線表示
  • 額外補充:元件依賴的表示方式
    • 上圖中上面那種也叫插座表示法,和下面的表示方式是等價的

1.2.6.1.6、前幾者組合在一起的樣子

外面大的那個就是容器,和包圖很像(但不太一樣)

既然提到了包圖,那就一次性弄完:

  • 包圖:見名知意。和平時接觸的包依賴關係一樣。如:A包匯入B包,那A包可以使用B包的東西
    • 單個包圖完整樣子是下面這個鬼樣

搞到了包圖,那就解釋一下前面聯結器那裡:為什麼聯結器是通用的問題

  • 為什麼聯結器可以通用?有個包關係是如下的樣子
    • 即:混合結構(Composite Structures)中匯入了類圖(classes),後面的依次看,繼而推出:混合結構(Composite Structures) 是類圖(classes)的一種擴充套件,同理:元件圖中就有了混合結構和類

1.2.6.1.7、混合結構

上面提到了混合結構,那也來搞一下

混合結構的意思就是字面意思,混合嘛,即:類圖、元件圖......混合使用(開發中的那個畫法就是)

  • 解讀:
    • 整個Car大框就是類圖,類圖中的屬性(Car下面的那個屬性的大框)變成了元件圖(元件圖中再套元件圖.......),元件圖中的屬性表示方式和類圖中一樣(-為private、+為public,屬性名、屬性型別.....)

1.2.6.1.8、元件圖範例
  • 在網上嫖的圖,意思意思


1.2.7、部署圖

定義:描述的就是物理層面的模型,就是讓系統和硬體打上交道


部署圖與元件圖相同的構成元素:

  • 元件、介面、元件範例,提供介面(元件向外提供服務)、需求介面(元件要求外部提供的服務)

部署圖與構件圖的關係:

  • 部署圖表現元件範例; 元件圖表現元件型別的定義

  • 部署圖偏向於描述元件在節點中執行時的狀態,描述了元件執行的環境

  • 元件圖偏向於描述元件之間相互依賴支援的基本關係

上面提到了元件和元件範例,其實只是不同的稱呼而已,在元件圖中都已經見過,只是換成部署圖中名字有細微區別而已,符號都是一樣的



1.2.7.1、部署圖的組成

1.2.7.1.1、物件 / 構建 / 元件

定義:就是被部署的東西

長什麼樣子在元件圖中已經見過了(參考:1.2.6.1.1、元件)


1.2.7.1.2、節點

定義:執行時物件和元件範例駐留的位置(把方向放大一點:也可以說是物件要部署的目標位置,即:物件要部署到哪裡去)


節點畫法:



針對於各物件 / 各節點的部署來說:描述的各節點之間的關係(也叫範例層部署)

  • 1、前面玩過的那些類圖、物件圖、元件圖中的關係都可以用,部署圖就看部署的是什麼
    • 各物件關係部署(就是物件圖簡化版,換個意思,換個場景[部署],細微換一下畫法罷了),支援:一對多、多對多
      • 官方檔案中是這樣介紹這種部署的
  • 2、各節點關係部署,支援:一對多、多對多
    • 再加一點東西就變成官方檔案中說的:節點範例部署,支援:一對多、多對多

針對節點以及其包含的元件的部署(這種也叫描述層部署),官方檔案稱之為:元件 / 工件部署(工件:指的是任何留下記錄的事物都可以稱之為工件),叫法無所謂,支援:一對多、多對多


1.2.7.1.3、物件與結點的關係:部署

就是下圖中元件和整個節點的關係(deploy)

  • 如官方檔案中

1.2.7.1.4、結點與結點的關係:通訊路徑

指的就是下圖中的那根線+通訊方式




2、設計原則

單一職責(Single Responsibility Principle):一個類和方法只做一件事(有且僅有一個原因引起它的[類和介面]改變)

開閉原則(Open Closed Principle):抽象架構,擴充套件實現

里氏替換(Liskov Substitution Principle):多型,子類可以擴充套件父類別

迪米特原則(Law of Demeter):最少知道,降低藕合

介面隔離(Interface Segregation Principle):建立單一介面 / 介面不可再分

依賴倒置(Dependence Inversion Principle):細節依賴抽象,下層依賴上層(也可以叫做面向介面程式設計)

以上幾個的第一個單詞首字母合起來就是(里氏替換原則和迪米特法則的首字母重複,只取一個): SOLID(堅硬的)


2.1、單一職責原則

定義:一個類和方法只做一件事(有且僅有一個原因引起它的[類和介面]改變)


如打電話分為:撥通、交流、結束通話,類圖如下

  • 但是這樣是有問題的,因為:dial撥通、hangup結束通話是屬於連結管理,而chat交流是屬於資料傳送,因為這個IPhone介面就有兩個原因導致它發生變化了,因此:進行改造,抽離(面向介面程式設計,對外公佈介面,不公佈實現類)

2.2、開閉原則

定義:抽象架構,擴充套件實現

具體意思:指一個軟體實體如類、模組和函數應該對擴充套件開放,對修改關閉。也就是說一個軟體實體應該通過擴充套件來實現變化,而不是通過修改已有的程式碼來實現變化


如書店賣書,類圖如下:

    • 接下來搞打折活動,這時不可能說是去修改IBook介面,在裡面新增一個打折getOffPrice()的方法吧,這樣的話,那實現類NovelBook需要改原始碼,而關聯類BookStroe也需要修改。甚至也不可能直接在實現類NovelBook中把getPrice()的邏輯進行修改,這兩個方法都不行,因此:應該改成如下的樣子
      • 重新來一個OffNovelBook類繼承NovelBook,從而重寫getPrice()方法,這樣就做到:對擴充套件開放[原價格可以在NovelBook類的getPrice()中拿到,打折後的價格可以在擴充套件類OffNovelBookgetPrice()方法中拿到]、對修改關閉[並不需要動原有的程式碼]

2.3、里氏替換原則

定義:多型,子類可以擴充套件父類別(所以得知:針對的是繼承)

里氏替換原則通俗易懂的定義是:只要父類別能出現的地方,子類就可以出現,而且替換為子類也不會產生任何錯誤或異常(在開發中的場景是:定義一個介面或抽象類,然後在使用其實現類時,傳入介面型別的引數,即:多型),但是:又包含4層含義

  • 1、子類必須完全實現父類別的方法
  • 2、子類可以有自己的屬性和方法
  • 3. 覆蓋或實現父類別的方法時,輸入引數可以被放大(如:父類別中方法的引數型別是hashMap,子類重寫時引數型別可以Map型別)
  • 4. 覆蓋或實現父類別的方法時,輸出結果可以被縮小(對照3)

2.4、迪米特原則

定義:也叫最少知道原則,即:一個物件對其他物件應該有最少的瞭解,有另外一個英文解釋:Only talk to your immediate friends(只和直接的朋友交流)

核心觀念就是類間解耦,弱耦合,只有弱耦合了以後,類的複用率才可以提升上去

例子的話:生活中最常見,即:腳踏兩隻船,針對於兩個女票之間,這兩個物件最好還是少知道點彼此的事情比較好吧,不然試試?


2.5、介面隔離原則

定義:建立單一介面 / 介面不可再分,或者說是:類間的依賴關係應該建立在最小的介面上


如搜尋美女,類圖設計假如是下面的樣子

  • 但是如果按上圖來弄的話,那IBeauty美女介面中定義的是:好顏值 or 好身材 or 好氣質的才是美女,可是:每個人的品味都不一樣啊,可能有人認為這三者中的任意一種組合就叫美女,因此:需要把上面的類圖改造成如下的樣子:
    • 重構以後,不管以後需要顏值美女,還是需要身材美女,亦或氣質美女,都可以保持介面的穩定性
    • 以上把一個臃腫的介面拆分為三個獨立的介面所依賴的原則就是介面隔離原則,即:最小介面 / 建立單一介面嘛

2.6、依賴倒置原則

定義:細節依賴抽象,下層依賴上層(也可以叫做面向介面程式設計)

  • 1、模組間的依賴通過抽象發生,實現類之間不直接發生依賴關係,其依賴關係是通過介面或抽象類產生的;

  • 2、介面或抽象類不依賴於實現類;

  • 3、實現類依賴介面或抽象類

    依賴倒置原則可以減少類間的耦合性,提高系統的穩定性,降低並行開發引起的風險,提高程式碼的可讀性和可維護性


依賴倒置原則的使用建議:

  • 1、每個類儘量都有介面或抽象類,或者介面和抽象類兩者都具備。

  • 2、變數的表面型別儘量是介面或抽象類。

  • 3、任何類都不應該從具體類派生。

  • 4、儘量不要重寫基礎類別的方法。如果基礎類別是一個抽象類,而且這個方法已經實現了,子類儘量不要重寫。

  • 5、結合里氏替換原則使用




3、設計模式

分類:

  • 注:使用設計模式的規範,類名 = 需求名+使用的對應設計模式名,如:StringBuilder,這就是使用了Builder建造者模式

  • 設計模式不是一成不變的,主要是思想,至於形不需要在意,形只是便於理解罷了


3.1、建立型

這個型別的模式是專門針對於建立物件的,也就是它的適用機制


3.1.1、單例模式

定義:保證物件全域性唯一,即:保證一個類只有一個範例,哪怕是多執行緒來進行存取,向外提供一個存取此範例的方法即可

使用場景

  • 1、資料庫連線池不會反覆建立
  • 2、Spring中一個單例模式Bean的建立
  • 3、開發中設定一些全域性的屬性進行儲存(當然:用Redis更好)

3.1.1.1、static實現

package com.zixieqing.o1static;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>@description  : 該類功能  使用static的方式</p>
 * <p>@package      : com.zixieqing.o1static</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class StaticSingleton {

    private static Map<String, String> CACHE = new HashMap<String, String>();
}

  • 這種方式在第一次執行時就初始化Map了,不需要延遲載入
  • 缺點:需要被繼承 或 需要維持一些特定狀態時就不適合了

3.1.1.2、懶漢模式

定義:體現在一個「懶」字上,即:需要時才去建立物件

package com.zixieqing.o2lazy;

/**
 * <p>@description  : 該類功能  懶漢式
 *  此種方式不安全:好比多個人搶廁所,會造成不安全,可能有多個人搶到
 * </p>
 * <p>@package      : com.zixieqing.o2lazy</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class LazySingleton {

    /**
     * 1、private static 當前類的屬性
     */
    private static LazySingleton LAZY_SINGLETON_INSTANCE;

    /**
     * 2、private 的構造
     */
    private LazySingleton() {
    }

    /**
     * 3、提供public static 建立當前類物件的方法
     */
    public static LazySingleton getInstance() {
        if (LAZY_SINGLETON_INSTANCE != null) return LAZY_SINGLETON_INSTANCE;

        LAZY_SINGLETON_INSTANCE = new LazySingleton();
        return LAZY_SINGLETON_INSTANCE;

    }

    /**
     * 4、要想稍微安全就加synchronized同步鎖
     * 但是:此種方式因為把synchronized加在了方法上,導致所有存取爭鎖而出現 資源的浪費
     */
/*    public static synchronized lazy_unsafe_singleton getInstance() {
        if (LAZY_UNSAFE_INSTANCE_SINGLETON != null) return LAZY_UNSAFE_INSTANCE_SINGLETON;

        LAZY_UNSAFE_INSTANCE_SINGLETON = new Singleton_lazy_unsafe();
        return LAZY_UNSAFE_INSTANCE_SINGLETON;

    }*/
}


3.1.1.3、餓漢模式

定義:體現在「餓」字上,即:一開始就初始化

package com.zixieqing.o3hunger;

/**
 * <p>@description  : 該類功能  餓漢式實現
 * 這種方式和利用static的方式是異曲同工的
 * </p>
 * <p>@package      : com.zixieqing.o3hunger</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class HungerSingleton {

    private static HungerSingleton HUNGER_SINGLETON_INSTANCE = new HungerSingleton();

    private HungerSingleton() {}

    /**
     * 這種方式其實也不安全
     * 因為當多執行緒在if判斷時如果在同一時刻二者都判斷成立,就會建立不同的範例
     */
    public static HungerSingleton getInstance() {
        if (HUNGER_SINGLETON_INSTANCE != null) return HUNGER_SINGLETON_INSTANCE;

        HUNGER_SINGLETON_INSTANCE = new HungerSingleton();
        return HUNGER_SINGLETON_INSTANCE;
    }
}


3.1.1.4、內部類

package com.zixieqing.o4innerclass;

/**
 * <p>@description  : 該類功能  使用內部類實現 - 推薦的一種</p>
 * <p>@package      : com.zixieqing.o4innerclass</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class InnerClassSingleton {

    /**
     * 1、private的構造
     */
    private InnerClassSingleton() {}

    /**
     * 2、private static的內部類
     * 巧妙之處:呼叫這個類才會初始化,也就才能獲得InnerClassSingleton範例物件
     *         同時裡面的屬性使用static修飾,則:做到執行緒安全,也巧妙藉助了第一種實現方式:使用static的形式
     */
    private static class NewInstance{
        public static InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }

    /**
     * 3、public static對外提供獲取當前類範例的方法
     */
    public static InnerClassSingleton getInstance() {
        return NewInstance.INSTANCE;
    }
}


3.1.1.5、雙重鎖驗證

package com.zixieqing.o5twinlock;

/**
 * <p>@description  : 該類功能  雙重鎖校驗(執行緒安全)</p>
 * <p>@package      : com.zixieqing.o5twinlock</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class TwinLockSingleton {

    private static TwinLockSingleton INSTANCE;

    private TwinLockSingleton() {}

    public static TwinLockSingleton getInstance() {

        if (null != INSTANCE) return INSTANCE;

        // 雙重驗證:synchronized 和 if
        synchronized (TwinLockSingleton.class) {
            if (null == INSTANCE) return INSTANCE = new TwinLockSingleton();
        }
        return INSTANCE;
    }
}


3.1.1.6、CAS

package com.zixieqing.o6cas;

import java.util.concurrent.atomic.AtomicReference;

/**
 * <p>@description  : 該類功能  利用CAS演演算法實現
 * 好處:CAS的忙等演演算法是靠底層硬體,所以:保證了執行緒安全 和 不會產生執行緒的切換和阻塞的開銷,從而提高效能
 *      並且:可以支援較大的並行性
 * </p>
 * <p>@package      : com.zixieqing.o6cas</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class CASSingleton {

    private static CASSingleton CAS_SINGLETON_INSTANCE;

    /**
     * AtomicReference<K> 原子參照  儲存「一個」K範例
     */
    private static final AtomicReference<CASSingleton> INSTANCE = new AtomicReference<>();

    public static CASSingleton getInstance() {

        /*
            缺點就在這裡:CAS的忙等   從而造成:如果一直沒有獲取就會處於死循壞當中
         */
        while (true) {
            CAS_SINGLETON_INSTANCE = INSTANCE.get();
            if (null != CAS_SINGLETON_INSTANCE) return CAS_SINGLETON_INSTANCE;
        /*
            boolean compareAndSet(V expect, V update)
                expect 預期值
                update 要改成的新值
                如果當前值和預期值相等,那麼就以原子的方式將值改為新值

             下列邏輯:期望INSTANCE是null,所以將INSTANCE的值改為new Singleton_CAS()
         */
            INSTANCE.compareAndSet(null, new CASSingleton());
            // 獲取INSTANCE的值 返回值就是AtomicReference<Singleton_CAS>中的泛型型別
            return INSTANCE.get();
        }
    }
}


3.1.1.6、列舉

package com.zixieqing.o7num;

/**
 * <p>@description  : 該類功能  使用列舉來實現(極度推薦)
 * </p>
 * <p>@package      : com.zixieqing.o7num</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public enum EnumSingleton {

    /**
     * 物件
     */
    INSTANCE;

    /**
    * 根據需要自行設定getter也行
    */
}


3.1.2、原型模式

定義:根據一個已有物件(原型範例) 建立 新的物件(就是克隆)

解決的問題:建立重複物件,而這部分物件本身比較複雜,生成過程從庫或者RPC介面中獲取資料的耗時時長可能會很長,因此:採用克隆的方式會節省時間,總之:當系統中需要建立相同或相似的物件時,就可以用原型模式


場景(在開發中貌似都沒用到過):

  • 1、CV大師(ctrl+cctrl+v
  • 2、JavaSE中的Object.clone()

瞭解兩個名詞:淺拷貝和深拷貝

  • 淺拷貝
    • 1、當類的成員變數是基本資料型別時,淺拷貝會將原物件的屬性值賦值給新物件
    • 2、當類中成員變數是參照資料型別時,淺拷貝 會將 原物件的參照資料型別的地址 賦值給新物件的成員變數。也就是說 兩個物件共用了同一個資料。當其中一個物件修改成員變數的值時,另外一個的值也會隨之改變
  • 深拷貝
    • 無論是 基本資料型別還是參照資料型別,都會去開闢額外的空間給新物件

3.1.2.1、用Object.clone() API(不推薦)

3.1.2.1.1、克隆基本資料型別
  • 淺拷貝會將原物件的屬性值賦值給新物件(拷貝的是值)

  • 注:String底層被final修飾了的,修改值之後是重新建立了一個Sting物件,修改之後不會影響原物件

package com.zixieqing;

/**
 * <p>@description  : 該類功能  原型類(屬性都是基本資料型別時)
 * Cloneable 標誌Object.clone()方法可以對Person該類的範例進行欄位的複製
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class Person implements Cloneable{

    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = null;
        try {
            person = (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return person;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

package com.zixieqing;


/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : MeiChengsong</p>
 * <p>@version      : V1.0.0</p>
 */

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Person person = new Person();
        person.setName("紫邪情");

        Person clonePerson = (Person) person.clone();
        System.out.println( "原型物件:" + person);
        System.out.println( "克隆物件:" + clonePerson);

        clonePerson.setName("小紫");

        System.out.println("==========修改之後=============");
        System.out.println(person);
        System.out.println(clonePerson);

    }
}


3.1.2.1.2、克隆參照資料型別
  • 淺拷貝 會將 原物件的參照資料型別的地址 賦值給新物件的成員變數(拷貝的是地址)。也就是說 兩個物件共用了同一個資料。當其中一個物件修改成員變數的值時,另外一個的值也會隨之改變
package com.zixieqing;

/**
 * <p>@description  : 該類功能  原型類(屬性是參照資料型別時)
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class Peron2 implements Cloneable{
    private Person person;

    public Peron2() {
    }

    public Peron2(Person person) {
        this.person = person;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Peron2 peron2 = null;

        try {
            peron2 = (Peron2) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return peron2;
    }

    @Override
    public String toString() {
        return "Peron2{" +
                "person=" + person +
                '}';
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }
}

package com.zixieqing;


/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : MeiChengsong</p>
 * <p>@version      : V1.0.0</p>
 */

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Peron2 peron2 = new Peron2();
        Person person = new Person("法外狂徒");
        peron2.setPerson(person);

        Peron2 clonePerson2 = (Peron2) peron2.clone();

        System.out.println(peron2);
        System.out.println(clonePerson2);

        person.setName("張三");
        clonePerson2.setPerson(person);

        System.out.println("修改之後");
        System.out.println(peron2);
        System.out.println(clonePerson2);
    }
}


3.1.2.2、使用序列化(推薦)

package com.zixieqing.o2useserialize;

import com.zixieqing.o1useclone.Person;

import java.io.*;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing.o2useserialize</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class Test {

    public static void main(String[] args) throws IOException {

        Person3 person3 = new Person3(0, new Person("紫邪情"));
        Person3 cloneInstance = cloneInstance(person3);

        System.out.println(person3);
        System.out.println(cloneInstance);

        cloneInstance.setSex(1);

        System.out.println("===========修改之後=============");

        System.out.println(person3);
        System.out.println(cloneInstance);
    }


    /**
     * <p>@description  : 該方法功能 物件序列化克隆
     * </p>
     * <p>@methodName   : cloneInstance</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param person3 要進行序列化克隆的物件
     * @return com.zixieqing.o2useserialize.Person3
     */
    private static Person3 cloneInstance(Person3 person3) throws IOException {
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis;
        ObjectInputStream ois;
        Person3 person = null;

        try {
            // 序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(person3);

            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            person = (Person3) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != bos) bos.close();

            if (null != oos) oos.close();
        }

        return person;
    }
}


3.1.3、工廠模式

解決的問題:就是平時寫程式碼時,new範例物件的情況,因此:要是需要new範例物件時就可以考慮是否採用工廠模式,從而提高擴充套件性,減少以後的改程式碼量


3.1.3.1、簡單工廠(工廠基礎)

定義:把對類的建立初始化全都交給一個工廠來執行,而使用者不需要關心建立的過程是什麼樣的,只需要告訴工廠,我想要什麼就行了

設計模式中並沒有所謂的簡單工廠,這玩意兒嚴格來說是一種編碼規範,但是:也是學工廠模式的基礎

簡單工廠的角色

  • 抽象產品:定義產品的規則,即產品有哪些特性和功能,可以是介面、抽象類、普通類也行(但一般不會這麼幹)
  • 具體產品:實現或繼承抽象產品的子類
  • 產品工廠:提供建立產品的方法,讓使用者通過該方法獲取產品

3.1.3.1.1、簡單邏輯

開發場景:網上買商品,假設有三種購買方式(前面的數位對應其型別):1、通過優惠卡;2、通過快播兌換卡;3、啥也沒用,直接購買實物商品,根據前面說的三角色來整活


準備工作:依賴匯入

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- LOGGING begin -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.0.9</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>


1、抽象產品:定義規則

package com.zixieqing.o1simplefactory.o1simplelogic;

/**
 * <p>@description  : 該類功能  抽象產品:購物
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IShopping {

    /**
     * <p>@description  : 該方法功能 傳送商品
     * </p>
     * <p>@methodName   : sendCommodity</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param userId 使用者id
     * @param goodsName  商品名
     */
    void sendCommodity(String userId, String goodsName) throws Exception;
}


2、具體產品:實現或繼承抽象產品的子類

  • 優惠卡方式

    • 
        package com.zixieqing.o1simplefactory.o1simplelogic.impl;
      
        import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        /**
         * <p>@description  : 該類功能  具體商品:優惠卡
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class CouponService implements IShopping {
      
            private Logger logger = LoggerFactory.getLogger(CouponService.class);
      
            @Override
            public void sendCommodity(String userId, String goodsName) throws Exception {
                logger.info("使用者:{},通過優惠卡xxxxxx,購買了:{}", userId, goodsName);
            }
        }
      
      
  • 快播兌換卡方式

    • 
        package com.zixieqing.o1simplefactory.o1simplelogic.impl;
      
        import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        /**
         * <p>@description  : 該類功能  具體商品:快播兌換卡
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class QvodCardService implements IShopping {
      
            private Logger logger = LoggerFactory.getLogger(QvodCardService.class);
      
            @Override
            public void sendCommodity(String userId, String goodsName) throws Exception {
                logger.info("使用者:{},通過快播兌換卡yyyyy,購買了:{}", userId, goodsName);
            }
        }
      
      
  • 實物夠買的方式

    • 
        package com.zixieqing.o1simplefactory.o1simplelogic.impl;
      
        import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        /**
         * <p>@description  : 該類功能  具體商品:啥也不用,直接實物購買
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class GoodsService implements IShopping {
      
            private Logger logger = LoggerFactory.getLogger(GoodsService.class);
      
            @Override
            public void sendCommodity(String userId, String goodsName) throws Exception {
                logger.info("使用者:{},實物購買了:{}", userId, goodsName);
            }
        }
      
      

3、產品工廠:提供建立產品的方法,讓呼叫者通過該工廠獲取產品

package com.zixieqing.o1simplefactory.o1simplelogic;

import com.zixieqing.o1simplefactory.o1simplelogic.impl.CouponService;
import com.zixieqing.o1simplefactory.o1simplelogic.impl.GoodsService;
import com.zixieqing.o1simplefactory.o1simplelogic.impl.QvodCardService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>@description  : 該類功能  產品工廠:購物工廠
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o1simplelogic</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class ShoppingFactory {

    private Logger logger = LoggerFactory.getLogger(ShoppingFactory.class);

    /**
     * <p>@description  : 該方法功能 購物
     * </p>
     * <p>@methodName   : shopping</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param type  購物方式 1、優惠卡;2、快播兌換卡;3、實物購買
     * @return com.zixieqing.o1simplefactory.o1simplelogic.IShopping
     */
    public IShopping shopping(Integer type) {
        if (null == type) return null;

        logger.info("正在揀貨.....");

        if (1 == type) return new CouponService();

        if (2 == type) return new QvodCardService();

        if (3 == type) return new GoodsService();

        throw new RuntimeException("不存在的商品服務型別");
    }
}

  • 注:如果上面這個工廠的方法加上了static就變成了靜態工廠(靜態方法能被繼承、但不能被重寫)

4、測試:呼叫者通過工廠獲取對應產品

package com.zixieqing;

import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
import com.zixieqing.o1simplefactory.o1simplelogic.ShoppingFactory;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class APITest {

    public static void main(String[] args) throws Exception{

        ShoppingFactory shoppingFactory = new ShoppingFactory();

        // 1、優惠卡型別
        IShopping shopping_1 = shoppingFactory.shopping(1);
        shopping_1.sendCommodity(System.nanoTime()+"", "充氣娃娃");

        System.out.println("================華麗的分割線===================");

        // 2、快播兌換卡
        IShopping shopping_2 = shoppingFactory.shopping(2);
        shopping_2.sendCommodity(System.nanoTime()+"", "AI女票");

        System.out.println("================華麗的分割線===================");

        IShopping shopping_3 = shoppingFactory.shopping(3);
        shopping_3.sendCommodity(System.nanoTime()+"", "枸杞");

    }
}


上面就是理解簡單工廠的邏輯,總結一丟丟

  • 簡單工廠的邏輯:
  • 由上圖也可知,簡單工廠優點就是呼叫者可以免除直接建立產品物件的責任,而僅僅"消費"產品,明確責任邊界,降低耦合性,當然其缺點也很明顯
    • 1、違背了開閉原則
      • 所以從上圖可知:簡單工廠就是橫向發展(不斷加實現類、工廠類中不斷加邏輯判斷)

3.1.3.1.2、更加貼合開發場景的邏輯

1、先決條件:先簡單搞個返回結果集的工具類

package com.zixieqing.o1simplefactory.o2complex.util;

/**
 * <p>@description  : 該類功能  返回結果集工具類
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex.util</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class ResultUtil {

    /**
     * 編碼
     */
    private String code;

    /**
     * 資訊
     */
    private String info;

    public ResultUtil(String code, String info) {
        this.code = code;
        this.info = info;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}


2、優惠卡服務

package com.zixieqing.o1simplefactory.o2complex.coupon;

import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>@description  : 該類功能  模擬發放優惠券業務
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex.coupon</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class CouponService {

    private Logger logger = LoggerFactory.getLogger(CouponService.class);

    /**
     * <p>@description  : 該方法功能 發放優惠券
     * </p>
     * <p>@methodName   : sendCoupon</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param uId 使用者id
     * @param couponNumber 分配的優惠券號碼
     * @param uuid  隨機生成的uuid號
     * @return com.zixieqing.o1simplefactory.o2complex.util.ResultUtil
     */
    public ResultUtil sendCoupon(String uId, String couponNumber, String uuid) {
        logger.info("發放優惠券業務準備啟動..........");

        logger.info("使用者:{},獲得了優惠券:{}", uId, uId + couponNumber + uuid);

        return new ResultUtil("0000", "優惠券發放成功");
    }
}


3、快播兌換卡業務

package com.zixieqing.o1simplefactory.o2complex.qvod;

import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>@description  : 該類功能  快播兌換卡業務
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex.qvod</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class QvodService {

    private Logger logger = LoggerFactory.getLogger(QvodService.class);

    /**
     * <p>@description  : 該方法功能 授予兌換卡
     * </p>
     * <p>@methodName   : grentToken</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param phone 使用者手機號
     * @param cardId  隨機生成的卡號
     * @return com.zixieqing.o1simplefactory.o2complex.util.ResultUtil 
     */
    public ResultUtil grentToken(String phone, String cardId) {
        logger.info("授予的兌換卡為:{}", phone + cardId);
        return new ResultUtil("0000", phone + cardId);
    }
}


4、實物購買商品業務

  • 輔助物件

    • 
        package com.zixieqing.o1simplefactory.o2complex.goods;
      
        /**
         * <p>@description  : 該類功能  實物購買:支付要求物件
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.goods</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class DeliverRequest {
      
      
            /**
             * 使用者姓名
             */
            private String userName;
            /**
             * 使用者手機
             */
            private String userPhone;
            /**
             * 商品SKU:庫存保有單位
             */
            private String sku;
            /**
             * 訂單ID
             */
            private String orderId;
            /**
             * 收貨人姓名
             */
            private String consigneeUserName;
            /**
             * 收貨人手機
             */
            private String consigneeUserPhone;
            /**
             * 收穫人地址
             */
            private String consigneeUserAddress;
      
            public String getUserName() {
                return userName;
            }
      
            public void setUserName(String userName) {
                this.userName = userName;
            }
      
            public String getUserPhone() {
                return userPhone;
            }
      
            public void setUserPhone(String userPhone) {
                this.userPhone = userPhone;
            }
      
            public String getSku() {
                return sku;
            }
      
            public void setSku(String sku) {
                this.sku = sku;
            }
      
            public String getOrderId() {
                return orderId;
            }
      
            public void setOrderId(String orderId) {
                this.orderId = orderId;
            }
      
            public String getConsigneeUserName() {
                return consigneeUserName;
            }
      
            public void setConsigneeUserName(String consigneeUserName) {
                this.consigneeUserName = consigneeUserName;
            }
      
            public String getConsigneeUserPhone() {
                return consigneeUserPhone;
            }
      
            public void setConsigneeUserPhone(String consigneeUserPhone) {
                this.consigneeUserPhone = consigneeUserPhone;
            }
      
            public String getConsigneeUserAddress() {
                return consigneeUserAddress;
            }
      
            public void setConsigneeUserAddress(String consigneeUserAddress) {
                this.consigneeUserAddress = consigneeUserAddress;
            }
        }
      
      
  • 業務

    •   package com.zixieqing.o1simplefactory.o2complex.goods;
      
        import com.alibaba.fastjson.JSON;
        import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        /**
         * <p>@description  : 該類功能  實物購買商品業務
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.goods</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class GoodsService {
      
            private Logger logger = LoggerFactory.getLogger(GoodsService.class);
      
            /**
             * <p>@description  : 該方法功能 發貨
             * </p>
             * <p>@methodName   : deliverGoods</p>
             * <p>@author: ZiXieqing</p>
             * <p>@version: V1.0.0</p>
             * @param deliverRequest  輔助:支付物件
             * @return com.zixieqing.o1simplefactory.o2complex.util.ResultUtil 
             */
            public ResultUtil deliverGoods(DeliverRequest deliverRequest) {
                logger.info("模擬傳送實物商品一個:{}", JSON.toJSONString(deliverRequest));
                return new ResultUtil("0000", "發貨成功:" + deliverRequest);
            }
        }
      
      

5、抽象產品:定義規則

package com.zixieqing.o1simplefactory.o2complex;

import java.util.Map;

/**
 * <p>@description  : 該類功能  商品
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface ICommodityService {

    /**
     * <p>@description  : 該方法功能 傳送商品
     * </p>
     * <p>@methodName   : sendCommodity</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param uId 使用者id
     * @param commodityId 商品id
     * @param bizId 業務id
     * @param extMap  擴充套件資訊
     */
    void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
}


6、具體商品:實現或繼承抽象產品的子類

  • 優惠卡

    •   package com.zixieqing.o1simplefactory.o2complex.impl;
      
        import com.zixieqing.o1simplefactory.o2complex.ICommodityService;
        import com.zixieqing.o1simplefactory.o2complex.coupon.CouponService;
        import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        import java.util.Map;
        import java.util.UUID;
      
        /**
         * <p>@description  : 該類功能  TODO
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class CouponCommodityServiceImpl implements ICommodityService {
      
            private Logger logger = LoggerFactory.getLogger(CouponCommodityServiceImpl.class);
      
            /**
             * 模擬@autowried注入
             */
            private CouponService couponService = new CouponService();
      
            @Override
            public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
                ResultUtil result = couponService.sendCoupon(uId, commodityId, UUID.randomUUID().toString());
      
                if (!"0000".equals(result.getCode()))
                    throw new RuntimeException(result.getInfo());
            }
        }
      
      
  • 快播兌換卡

    •   package com.zixieqing.o1simplefactory.o2complex.impl;
      
        import com.zixieqing.o1simplefactory.o2complex.ICommodityService;
        import com.zixieqing.o1simplefactory.o2complex.qvod.QvodService;
        import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        import java.util.Map;
      
        /**
         * <p>@description  : 該類功能  快播兌換卡發貨
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class QvodCommodityServiceImpl implements ICommodityService {
      
            private Logger logger = LoggerFactory.getLogger(CouponCommodityServiceImpl.class);
      
            /**
             * 模擬注入
             */
            private QvodService qvodService = new QvodService();
      
            @Override
            public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
                // 這裡把電話號碼定死,模擬而已
                ResultUtil result = qvodService.grentToken("12345678910", bizId + commodityId);
      
                logger.info("通過快播兌換卡:{},獲取商品:{}", bizId + commodityId, commodityId);
      
                if (!"0000".equals(result.getCode()))
                    throw new RuntimeException(result.getInfo());
            }
        }
      
      
  • 實物購買

    •   package com.zixieqing.o1simplefactory.o2complex.impl;
      
        import com.zixieqing.o1simplefactory.o2complex.ICommodityService;
        import com.zixieqing.o1simplefactory.o2complex.goods.DeliverRequest;
        import com.zixieqing.o1simplefactory.o2complex.goods.GoodsService;
        import com.zixieqing.o1simplefactory.o2complex.util.ResultUtil;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
      
        import java.util.Map;
      
        /**
         * <p>@description  : 該類功能  實物購買商品
         * </p>
         * <p>@package      : com.zixieqing.o1simplefactory.o2complex.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class GoodsCommodityServiceImpl implements ICommodityService {
      
            private Logger logger = LoggerFactory.getLogger(GoodsCommodityServiceImpl.class);
      
            /**
             * 模擬注入
             */
            private GoodsService goodsService = new GoodsService();
      
            @Override
            public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
                DeliverRequest deliverRequest = new DeliverRequest();
                // 下面這些資訊去資料庫搞出來
                deliverRequest.setUserName("紫邪情");
                deliverRequest.setUserPhone("123143124342");
                deliverRequest.setSku(commodityId);
                deliverRequest.setOrderId(bizId);
                deliverRequest.setConsigneeUserName(extMap.get("consigneeUserName"));
                deliverRequest.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));
                deliverRequest.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));
      
                ResultUtil result = goodsService.deliverGoods(deliverRequest);
      
                if (!"0000".equals(result.getCode()))
                    throw new RuntimeException(result.getInfo());
            }
        }
      
      

7、產品工廠:提供建立產品的方法,讓呼叫者通過該方法獲取產品

package com.zixieqing.o1simplefactory.o2complex;

import com.zixieqing.o1simplefactory.o2complex.impl.CouponCommodityServiceImpl;
import com.zixieqing.o1simplefactory.o2complex.impl.GoodsCommodityServiceImpl;
import com.zixieqing.o1simplefactory.o2complex.impl.QvodCommodityServiceImpl;

/**
 * <p>@description  : 該類功能  產品工廠
 * </p>
 * <p>@package      : com.zixieqing.o1simplefactory.o2complex</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class CommodityFactory {

    /**
     * <p>@description  : 該方法功能 獲取產品
     * </p>
     * <p>@methodName   : getCommodity</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     * @param type  產品型別
     * @return com.zixieqing.o1simplefactory.o2complex.ICommodityService 
     */
    public ICommodityService getCommodity(Integer type) {
        if (1 == type) return new CouponCommodityServiceImpl();

        if (2 == type) return new QvodCommodityServiceImpl();

        if (3 == type) return new GoodsCommodityServiceImpl();

        throw new RuntimeException("不合法的商品型別");
    }
}


8、測試

package com.zixieqing;

import com.zixieqing.o1simplefactory.o1simplelogic.IShopping;
import com.zixieqing.o1simplefactory.o1simplelogic.ShoppingFactory;
import com.zixieqing.o1simplefactory.o2complex.CommodityFactory;
import com.zixieqing.o1simplefactory.o2complex.ICommodityService;

import java.util.UUID;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class APITest {

    public static void main(String[] args) throws Exception{

        System.out.println("================華麗的分割線:簡單結合業務開發進行測試===================");

        CommodityFactory commodityFactory = new CommodityFactory();

        // 1、優惠卡型別
        ICommodityService commodity_1 = commodityFactory.getCommodity(1);
        commodity_1.sendCommodity(System.nanoTime() + "",
                System.currentTimeMillis() + "",
                UUID.randomUUID() + "",
                null);

        // 2、快播兌換卡
        ICommodityService commodity_2 = commodityFactory.getCommodity(2);
        commodity_2.sendCommodity(System.nanoTime() + "",
                System.currentTimeMillis() + "",
                UUID.randomUUID() + "",
                null);

        // 3、實物購買
        ICommodityService commodity_3 = commodityFactory.getCommodity(3);
        HashMap<String, String> extMap = new HashMap<>();
        extMap.put("consigneeUserName", "紫邪情");
        extMap.put("consigneeUserPhone", "31343214321432");
        extMap.put("consigneeUserAddress", "浙江省.杭州市.餘杭區.XX街道.YY小區.324134321431");
        commodity_3.sendCommodity(System.nanoTime() + "",
                System.currentTimeMillis() + "",
                UUID.randomUUID() + "",
                extMap);
    }
}


3.1.3.1.3、構建出簡單工廠的方式

廢話文學

  • 1、思考要建立的幾個範例(具體產品)之間有什麼共通性
  • 2、將多個範例的共通性抽取成一個介面(抽象產品)
  • 3、使用一個工廠來對建立的範例進行判斷,從而讓呼叫者根據條件得到想要的範例物件(還可以結合反射來建立物件)
  • 4、然後根據下圖進行架構即可


3.1.3.2、工廠方法模式

定義:定義一個建立物件的介面,但由子類(具體工廠)決定要範例化的類是哪一個。工廠方法讓類把範例化推遲到子類


廢話文學:

  • 工廠方法模式的角色

    • 抽象工廠角色:工廠方法模式的核心,是具體工廠角色必須實現的介面或者繼承的父類別
    • 具體工廠角色:含有和具體業務邏輯有關的程式碼,由業務呼叫建立對應的具體產品的物件
    • 抽象產品角色:是具體產品繼承的父類別或者是實現的介面
    • 具體產品角色:具體工廠角色所建立的物件就是此角色的範例
  • 工廠方法模式這玩意兒其實就是在簡單工廠的基礎上稍微變了一下而已,多了一層罷了(沒什麼是加一層解決不了的,一層不行,那就再來一層),這裡加的這一層就是對業務層再抽象了一下而已

  • 簡單工廠是橫向發展(不斷橫向新增實現類),而工廠方法模式本質是為了解決簡單工廠模式的問題(違背開閉原則),所以優點和簡單工廠模式一樣,其是縱向發展(不斷縱向新增工廠類+實現類)

  • 簡單工廠模式

  • 工廠方法模式:

    • 從上圖可以看出:工廠方法模式做的是同一類產品(一條流水線開機時不會還搞多產品加工吧),這個點就是和下面抽象工廠的最大區別(抽象工廠針對的是產品簇)

3.1.3.2.1、Java中使用工廠方法模式的地方

這個東西其實在剛剛前面的程式碼中有一個東西就用到了,即:SLF4J紀錄檔門面,前面用了一個LoggerFactory,它裡面就用到了工廠方法模式

1、進入getLogger()

// 進入getLogger()
private Logger logger = LoggerFactory.getLogger(QvodService.class);


// 得到的程式碼
    public static Logger getLogger(Class clazz) {
        // 進入這裡的getLogger()
        return getLogger(clazz.getName());
    }

// 得到程式碼
    public static Logger getLogger(String name) {
        // 再看一下getILoggerFactory()
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }

// getILoggerFactory()的程式碼
    public static ILoggerFactory getILoggerFactory() {
        if (INITIALIZATION_STATE == 0) {
            INITIALIZATION_STATE = 1;
            performInitialization();
        }

        switch(INITIALIZATION_STATE) {
        case 1:
			// 重點1、static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
            return TEMP_FACTORY;
        case 2:
            throw new IllegalStateException("org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
        case 3:
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
        case 4:
			// 重點2、static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
            return NOP_FALLBACK_FACTORY;
        default:
            throw new IllegalStateException("Unreachable code");
        }
    }

  • 上面的兩個:TEMP_FACTORYNOP_FALLBACK_FACTORY都實現了ILoggerFactory

2、看一眼ILoggerFactory

// 這個就是根工廠:定義最大規則的那個叼毛
public interface ILoggerFactory {
    Logger getLogger(String var1);
}


逆向回來,邏輯就變成如下的樣子


廢話文學

  • 在開發中怎麼使用工廠方法模式(下列邏輯不止適用於此模式,建立型、行為型、結構型均適合,瞭解每種型別針對的是對什麼做架構即可,如:是對類中方法、還是類本身等等做架構來進行抽離,最後時間長了一看需求就知道咋個設計了)

    • 1、思考自己要建立的幾個範例( 即:具體產品)有什麼共通性

    • 2、將共通性抽取出來變成一個介面或抽象類(即:抽象產品)

    • 3、思考對每一個範例(產品)弄一個工廠類之後(即:具體工廠),那這幾個工廠類之間又有什麼共通性

    • 4、將工廠類的共通性抽取出來變成一個介面(即:抽象工廠)

    • 5、當然:再考慮以後應該會在什麼地方新增功能、是否會擴充套件的話更好,但這一條需要經驗積累

    • 6、然後使用下圖方式做架構即可


3.1.3.2.2、範例

場景:檔案解析,假設有兩種:1、xml檔案解析;2、json檔案解析

邏輯如下


1、抽象產品

package com.zixieqing.o2factorymethod;

/**
 * <p>@description  : 該類功能  檔案解析器
 * </p>
 * <p>@package      : com.zixieqing.o2factorymethod</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IFileParser {

    String parse();
}


2、具體產品

  • json解析

    • 
        package com.zixieqing.o2factorymethod.impl;
      
        import com.zixieqing.o2factorymethod.IFileParser;
      
        /**
         * <p>@description  : 該類功能  json檔案解析器
         * </p>
         * <p>@package      : com.zixieqing.o2factorymethod.impl</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class JsonFileParse implements IFileParser {
            @Override
            public String parse() {
                return "這裡就搞json檔案解析的邏輯";
            }
        }
      
      
  • xml解析

    • 
        package com.zixieqing.o2factorymethod.impl;
      
        import com.zixieqing.o2factorymethod.IFileParser;
      
        /**
         * <p>@description  : 該類功能  xml檔案解析器
         * </p>
         * <p>@package      : com.zixieqing.o2factorymethod</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class XMLFileParse implements IFileParser {
            @Override
            public String parse() {
                return "這裡就搞xml檔案的解析邏輯";
            }
        }
      
      

3、抽象工廠

package com.zixieqing.o2factorymethod.factory;

import com.zixieqing.o2factorymethod.IFileParser;

/**
 * <p>@description  : 該類功能  解析器抽象工廠
 * </p>
 * <p>@package      : com.zixieqing.o2factorymethod</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IParserFactory {

    IFileParser parse();
}


4、具體工廠

  • json解析工廠

    • 
        package com.zixieqing.o2factorymethod.factory.impl;
      
        import com.zixieqing.o2factorymethod.IFileParser;
        import com.zixieqing.o2factorymethod.factory.IParserFactory;
        import com.zixieqing.o2factorymethod.impl.JsonFileParse;
      
        /**
         * <p>@description  : 該類功能  json解析工廠
         * </p>
         * <p>@package      : com.zixieqing.o2factorymethod.factory</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class JsonParseFactory implements IParserFactory {
      
            @Override
            public IFileParser parse() {
                return new JsonFileParse();
            }
        }
      
      
  • xml解析工廠

    • 
        package com.zixieqing.o2factorymethod.factory.impl;
      
        import com.zixieqing.o2factorymethod.IFileParser;
        import com.zixieqing.o2factorymethod.factory.IParserFactory;
        import com.zixieqing.o2factorymethod.impl.XMLFileParse;
      
        /**
         * <p>@description  : 該類功能  xml解析工廠
         * </p>
         * <p>@package      : com.zixieqing.o2factorymethod.factory</p>
         * <p>@author       : ZiXieqing</p>
         * <p>@version      : V1.0.0</p>
         */
      
        public class XMLParseFactory implements IParserFactory {
            @Override
            public IFileParser parse() {
                return new XMLFileParse();
            }
        }
      
      

3.1.3.3、抽象工廠模式

定義:在工廠方法模式的基礎上,對工廠進行變化一下

解決的問題:產品簇的問題(一箇中心工廠來建立其他工廠)

對於產品簇百度有一個通俗易懂的解釋:指具有相同或相似的功能結構或效能,共用主要的產品特徵、元件或子結構,並通過變型設定來滿足特定市場的一組產品的聚類

廢話文學對產品簇的解釋:產品簇是指工廠生產出的產品們之間彼此具備強關聯。比如:AK47工廠生產的 AK47步槍、AK47專配的子彈,一旦 AK47裝錯了子彈是無法正常開槍的(甚至會炸膛)

抽象工廠模式的角色

  • 抽象工廠角色:工廠方法模式的核心,是具體工廠角色必須實現的介面或者繼承的父類別
  • 具體工廠角色:含有和具體業務邏輯有關的程式碼,由業務呼叫建立對應的具體產品的物件
  • 抽象產品角色:是具體產品繼承的父類別或者是實現的介面
  • 具體產品角色:具體工廠角色所建立的物件就是此角色的範例

抽象工廠模式邏輯舉例(上面的四角色自行對應)


3.1.3.3.1、簡單邏輯實現

就用上面說的傢俱來舉例


1、椅子介面(抽象產品)

package com.zixieqing.o3abstractfactory;

/**
 * <p>@description  : 該類功能  抽象產品:椅子介面
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IChair {

    /**
     * <p>@description  : 該方法功能 生產椅子
     * </p>
     * <p>@methodName   : createChair</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     *
     * @return java.lang.String
     */
    String create();
}

  • 1.1、木質桌子(具體產品)
package com.zixieqing.o3abstractfactory.impl;

import com.zixieqing.o3abstractfactory.IChair;

/**
 * <p>@description  : 該類功能  具體產品:木質椅子
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.impl</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class WoodenChair implements IChair {
    @Override
    public String create() {
        return "木質椅子";
    }
}

  • 1.2、塑料桌子(具體產品)
package com.zixieqing.o3abstractfactory.impl;

import com.zixieqing.o3abstractfactory.IChair;

/**
 * <p>@description  : 該類功能  具體產品:塑料椅子
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.impl</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class PlasticChair implements IChair {
    @Override
    public String create() {
        return "塑料椅子";
    }
}


2、桌子介面(抽象產品)

package com.zixieqing.o3abstractfactory;

/**
 * <p>@description  : 該類功能  抽象產品:桌子介面
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IDesk {

    /**
     * <p>@description  : 該方法功能 生產桌子
     * </p>
     * <p>@methodName   : create</p>
     * <p>@author: ZiXieqing</p>
     * <p>@version: V1.0.0</p>
     *
     * @return java.lang.String
     */
    String create();

}

  • 木質桌子
package com.zixieqing.o3abstractfactory.impl;

import com.zixieqing.o3abstractfactory.IDesk;

/**
 * <p>@description  : 該類功能  具體產品:木質桌子
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.impl</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class WoodenDesk implements IDesk {
    @Override
    public String create() {
        return "木質桌子";
    }
}

  • 塑料桌子
package com.zixieqing.o3abstractfactory.impl;

import com.zixieqing.o3abstractfactory.IDesk;

/**
 * <p>@description  : 該類功能  具體產品:塑料桌子
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.impl</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class PlastidDeck implements IDesk {
    @Override
    public String create() {
        return "塑料桌子";
    }
}


3、傢俱抽象工廠

package com.zixieqing.o3abstractfactory;

/**
 * <p>@description  : 該類功能  抽象工廠:傢俱工廠
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IFurnitureFactory {

    IChair createChair();

    IDesk createDesk();
}

  • 木質傢俱工廠
package com.zixieqing.o3abstractfactory.factory;

import com.zixieqing.o3abstractfactory.IChair;
import com.zixieqing.o3abstractfactory.IDesk;
import com.zixieqing.o3abstractfactory.IFurnitureFactory;
import com.zixieqing.o3abstractfactory.impl.WoodenChair;
import com.zixieqing.o3abstractfactory.impl.WoodenDesk;

/**
 * <p>@description  : 該類功能  具體工廠:專門生產木質傢俱這一產品簇需要的東西
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory.factory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class WoodenIFurnitureFactory implements IFurnitureFactory {
    @Override
    public IChair createChair() {
        return new WoodenChair();
    }

    @Override
    public IDesk createDesk() {
        return new WoodenDesk();
    }
}

  • 塑料傢俱工廠
package com.zixieqing.o3abstractfactory.factory;

import com.zixieqing.o3abstractfactory.IChair;
import com.zixieqing.o3abstractfactory.IDesk;
import com.zixieqing.o3abstractfactory.IFurnitureFactory;
import com.zixieqing.o3abstractfactory.impl.PlasticChair;
import com.zixieqing.o3abstractfactory.impl.PlastidDeck;

/**
 * <p>@description  : 該類功能  具體工廠:專門生產塑料傢俱這一產品簇需要的東西
 * </p>
 * <p>@package      : com.zixieqing.o3abstractfactory</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class PlasticFurnitureFactory implements IFurnitureFactory {
    @Override
    public IChair createChair() {
        return new PlasticChair();
    }

    @Override
    public IDesk createDesk() {
        return new PlastidDeck();
    }
}


4、測試

package com.zixieqing;

import com.zixieqing.o3abstractfactory.factory.PlasticFurnitureFactory;
import com.zixieqing.o3abstractfactory.factory.WoodenIFurnitureFactory;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class AbstractFactoryTest {

    public static void main(String[] args) {
        // 木質椅子
        String chair = new WoodenIFurnitureFactory().createChair().create();
        // 木質桌子
        String desk = new PlasticFurnitureFactory().createDesk().create();

        System.out.println(chair + "+" +desk);

        // 想要塑料傢俱這個產品簇的東西,就去找塑料傢俱工廠即可
    }
}


3.1.3.2、工廠方法與抽象工廠模式的區別

廢話文學

  • 1、首先兩個模式都可以生產產品 / 建立範例

  • 2、其次看前面的定義:

    • 工廠方法針對的是「某種產品」,繼而成為「某種產品A」 對應 「某工廠A」
    • 抽象工廠針對的是「某類產品」,繼而變成「某工廠B」 生產 「某類產品」(可以得到這類產品的範例,也可以得到這類產品的零部件)
  • 3、情景理解:

    • 工廠方法模式就像華為手機專賣店一樣,賣的是這一種手機
    • 抽象工廠模式就像手機售賣店一樣,可以買到華為、小米等手機,同時也可以根據自己需要買到自己手機的零部件
  • 4、在場景上(注意看下圖,兩個很像的,但注意看下圖中抽象工廠、抽象產品這兩個地方):

    • 工廠方法模式:它是讓某一條產品線上的產品更易被擴充套件(要擴充套件就是下圖加工廠和實現類唄)
    • 抽象工廠模式:它是讓多條產品線中,既保證這多條產品線中的單條產品線的產品更易被擴充套件的同時,又保證多條產品線之間遵循著同樣的約束

3.1.4、建造者模式

定義:將一個複雜物件的構建與表示分離,使得同樣的構建過程可以建立不同的表示;另外:建造者模式又叫生成器模式

換言之:將多個簡單物件通過一步一步組裝,最後變成複雜物件的過程;同時:簡單物件之間的組裝(相同的物料)通過相似的組裝過程可以得到不一樣的複雜物件(即:相同 / 相似的物料,不同的繁瑣組裝,得到不同的物件[此物件是一個複雜物件組合起來的])

典型例子:計算機有顯示器、滑鼠、鍵盤等等部件,不同使用者想要的電腦樣子、設定不一樣,但都只需要和售賣員講一下自己要的,最後店鋪就會組裝好使用者想要的電腦給使用者

注意:核心點是「相同 或 相似的組成成分」,即:建立物件需要很多步驟,但是步驟的順序不一定固定,這種場景才是使用建造者模式的最佳場景(開發經典例子:一個類的內部結構很複雜,如有很多屬性時,就可以考慮用建造者模式改造一下)


建造者角色

  • 產品(Product):就是最終要建立的產品物件
  • 抽象建造者(Builder):規範產品物件的各個組成部分的建造;可以是介面或抽象類;相當於:老闆,專門約束下級的
  • 具體建造者(ConcreteBuilder):實現或繼承抽象建造者的子類;是建立產品物件的執行者;相當於:真正幹活的人
  • 指揮者(Director):負責產品物件的構建次序(注:這個叼毛可有可無),就相當於:有圖紙的工程師;它和抽象建造者是關聯關係(即:這裡面的方法返回值型別是抽象建造者型別)
    • 可有可無的原因:呼叫者萬一就是個懂行的,那還要專門找個看圖紙的幹嘛,直接上場指揮產品應該按什麼順序構建就可以了
    • 由於這個指揮者可有可無,所以這個建造者模式的寫法相當靈活,建造者模式的核心是抽象建造者和具體建造者,其他的隨意變(結合其他設計模式、結合自己認為不影響邏輯的設計都可以),要麼介面+實現類,要麼抽象類+繼承類,但是也得注意:抽象建造者裡面的成員需要滿足的條件,即:方法或屬性的設計(換言之:就是依賴倒置原則,只是對屬性或方法進行了要求,從而做到讓物件的構建獨立(產品物件的屬性構建順序:可以通過指揮者、也可以直接完全交給呼叫者),表示分離(直接交給具體構建者,最後得到一個產品物件即可)

3.1.4.1、簡單邏輯

3.1.4.1.1、有指揮者的情況

注:以下邏輯不是固定的,可以隨意變化

  • 大體類圖如下

  • 1-1、產品物件
package com.zixieqing.o1simple;

/**
 * <p>@description  : 該類功能  產品物件:電腦
 * </p>
 * <p>@package      : com.zixieqing.o1simple</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class Computer {

    /**
     * 電腦主機
     */
    private String host;

    /**
     * 電腦顯示器
     */
    private String monitor;

    /**
     * 擴充套件資訊
     */
    private String extendMap;

    @Override
    public String toString() {
        return "Computer{" +
                "host='" + host + '\'' +
                ", monitor='" + monitor + '\'' +
                ", extendMap='" + extendMap + '\'' +
                '}';
    }

    public void setHost(String host) {
        this.host = host;
    }

    public void setMonitor(String monitor) {
        this.monitor = monitor;
    }

    public void setExtendMap(String extendMap) {
        this.extendMap = extendMap;
    }
}

  • 1-2、抽象建造者
package com.zixieqing.o1simple.builder;

import com.zixieqing.o1simple.Computer;

/**
 * <p>@description  : 該類功能  抽象建造者:老闆 約束下級
 * </p>
 * <p>@package      : com.zixieqing.o1simple</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IComputerBuilder {

    IComputerBuilder buildHost();

    IComputerBuilder buildMonitor();

    IComputerBuilder buildExtendMap();

    Computer getComputer();
}

  • 1-3、具體建造者
package com.zixieqing.o1simple.builder;

import com.zixieqing.o1simple.Computer;

/**
 * <p>@description  : 該類功能  具體建造者:幹活的員工
 * </p>
 * <p>@package      : com.zixieqing.o1simple.builder</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class ComputerBuilder implements IComputerBuilder{

    /**
     * 組合產品物件
     */
    private Computer computer = new Computer();


    /**
     * 產品物件零部件:建造主機
     */
    @Override
    public ComputerBuilder buildHost() {
        computer.setHost("主機設定");
      /*
        返回this就是成為鏈式呼叫的關鍵,如:
        computerBuilder.buildMonitor()
                        .buildHost()
                        .buildExtendMap()
                        .getComputer();
      */
        return this;
    }

    /**
     * 產品物件零部件:顯示器型別
     */
    @Override
    public ComputerBuilder buildMonitor() {
        computer.setMonitor("顯示器型別");
        return this;
    }

    /**
     * 產品物件零部件:額外資訊
     */
    @Override
    public ComputerBuilder buildExtendMap() {
        computer.setExtendMap("額外資訊");
        return this;
    }

    /**
     * 返回構建好的複雜物件 / 產品物件
     */
    @Override
    public Computer getComputer() {
        return computer;
    }
}

  • 1-4、指揮者
package com.zixieqing.o1simple.builder;

import com.zixieqing.o1simple.Computer;

/**
 * <p>@description  : 該類功能  指揮者:拿圖紙的工程師,負責產品物件的構建順序
 * </p>
 * <p>@package      : com.zixieqing.o1simple.builder</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class ComputerDirector {

    private static IComputerBuilder computerBuilder = new ComputerBuilder();

    public static Computer getComputer() {
        // 1、先搞顯示器
        return computerBuilder.buildMonitor()
                // 在搞主機設定(這裡能夠進行鏈式呼叫,就是因為具體建造者中建造各部件(屬性)時使用了return this的原因)
                .buildHost()
                // 最後弄其他設定資訊
                .buildExtendMap()
                .getComputer();
    }
}

  • 1-5、測試
package com.zixieqing;

import com.zixieqing.o1simple.builder.ComputerDirector;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class APITest {

    public static void main(String[] args) {

        System.out.println(ComputerDirector.getComputer());
    }
}

  • 1-6、結果
Computer{host='主機設定', monitor='顯示器型別', extendMap='額外資訊'}


3.1.4.1.2、沒有指揮者的情況

有的時候會面臨一種情況

  • 上面這個指揮者中的程式碼多此一舉了,這種應該直接交給呼叫者就行了,由呼叫者自己來安排產品物件的構建順序是怎麼樣的,因此:稍微變一下,把指揮者去掉,然後呼叫者直接去找建造者即可(抽象建造者、具體建造者程式碼不變,直接去掉指揮者),那呼叫就變成如下的樣子
package com.zixieqing;

import com.zixieqing.o1simple.Computer;
import com.zixieqing.o1simple.builder.ComputerBuilder;
import com.zixieqing.o1simple.builder.ComputerDirector;
import com.zixieqing.o1simple.builder.IComputerBuilder;

/**
 * <p>@description  : 該類功能  測試
 * </p>
 * <p>@package      : com.zixieqing</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public class APITest {

    public static void main(String[] args) {

        IComputerBuilder computerBuilder = new ComputerBuilder();

        Computer computer = computerBuilder.buildHost()
                .buildExtendMap()
                .buildMonitor()
                .getComputer();

        System.out.println(computer);
    }
}

  • 看到上面這個樣子是不是感覺熟悉了,就是JDK中StringBuilderappend()這個API的設計模式,當然:要是使用過MyBatis的xml開發模式,那裡面有一個使用SqlSessionFactoryBuilder來獲取SqlSessionFactory,這裡也用到了這個設計模式,甚至Spring底層的BeanDefinitionBuilder通過getBeanDefinition獲取BeanDefinition物件也用到了
        StringBuilder builder = new StringBuilder();

        builder.append("張三")
               .append("李四")
               .append("大刀王五")
               .append("王麻子");

  • 看到上面這種是自己傳值進去的,所以抽象建造者就又可以變一下了唄(產品物件的各部件物件的構建,值優呼叫者傳進去),如:
package com.zixieqing.o1simple.builder;

import com.zixieqing.o1simple.Computer;

/**
 * <p>@description  : 該類功能  抽象建造者:老闆 約束下級
 * </p>
 * <p>@package      : com.zixieqing.o1simple</p>
 * <p>@author       : ZiXieqing</p>
 * <p>@version      : V1.0.0</p>
 */

public interface IComputerBuilder {

    // 這樣甩手掌櫃就可以甩得稍微徹底點了
    IComputerBuilder buildHost(String host);

    IComputerBuilder buildMonitor(String monitor);

    IComputerBuilder buildExtendMap(String extendMap);

    Computer getComputer();
}

  • 甚至說:要是是類似於SpringBoot的自動轉配呢?需要的一些東西在組態檔中,可不可以結合工廠模式去進行載入呢(單例模式隨便一個地方都可以扔進去)?

4、說明

原本是打算將設計模式全部內容都放在這篇博文中的,但是內容太多了,只放建立型設計模式就快4000行了

造成的結果就是:自己新增內容卡頓,弄起來難受得很,而且內容太多載入起來也慢,所以決定將設計模式後續的行為型、結構型篇章單獨弄出來,後續釋出