電商系統(tǒng)設(shè)計模式實戰(zhàn)

1 代理模式
案例:根據(jù)文件類型,將文件存儲到不同服務(wù)
代理模式:給一個對象創(chuàng)建一個代理對象,通過代理對象可以使用該對象的功能。
CGLib和JDK是代理模式實現(xiàn)的技術(shù)方案。
1.1 文件服務(wù)應(yīng)用
代理模式的應(yīng)用場景除了代碼級別,還可以將代理模式遷移到應(yīng)用以及架構(gòu)級別,如下圖文件上傳代理服務(wù),針對一些圖片小文件,我們可以直接把文件存儲到FastDFS服務(wù),針對大文件,例如商品視頻介紹,我們可以把它存儲到第三方OSS。
用戶通過文件上傳代理服務(wù)可以間接訪問OSS和本地FastDFS,這種分布式海量文件管理解決方案,這里不僅在代碼層面充分運用了代理模式,在架構(gòu)層面也充分運用了代理模式。

1.2 分布式文件代理服務(wù)器實現(xiàn)
1.2.1 實現(xiàn)分析
基于代理模式,我們實現(xiàn)文件上傳分別路由到 aliyunOSS 和 FastDFS ,用例圖如下:

講解:
1、FileUpload抽象接口,定義了文件上傳方法,分別給它寫了2種實現(xiàn)。
2、AliyunOSSFileUpload是將文件上傳到aliyunOSS,主要上傳mp4和avi的視頻大文件。
3、FastdfsFileUpoad是將文件上傳到FastDFS,主要上傳png/jpg等圖片小文件。
4、FileUploadProxy是代理對象,供用戶訪問,調(diào)用了FileUpload的文件上傳方法,為用戶提供不同文件上傳調(diào)用。
5、FileController是控制器,用于接收用戶提交的文件,并調(diào)用代理FileUploadProxy實現(xiàn)文件上傳。
1.2.2 代碼實現(xiàn)
bootstrap.yml配置:


FileUpload接口定義:

AliyunOSSFileUpload實現(xiàn):


FastdfsFileUpoad實現(xiàn):


FileUploadProxy代理實現(xiàn):


FileController控制器實現(xiàn)

2 享元模式
定義:運用共享技術(shù)來有效地支持大量細粒度對象的復用。它通過共享已經(jīng)存在的對象來大幅度減少需要創(chuàng)建的對象數(shù)量、避免大量相似類的開銷,從而提高系統(tǒng)資源的利用率。
享元模式和單利的區(qū)別:
單例是對象只能自己創(chuàng)建自己,整個應(yīng)用中只有1個對象
享元模式根據(jù)需要共享,不限制被誰創(chuàng)建(有可能有多個對象實例)
優(yōu)點:
特定環(huán)境下,相同對象只要保存一份,這降低了系統(tǒng)中對象的數(shù)量,從而降低了系統(tǒng)中細粒度對象給內(nèi)存帶來的壓力。
缺點:
為了使對象可以共享,需要將一些不能共享的狀態(tài)外部化,這將增加程序的復雜性
2.1 享元模式實戰(zhàn)
案例:用戶下單,會話共享
2.1.1 會話跟蹤分析
會話跟蹤,如果是傳統(tǒng)項目用Session或者是Cookie,全項目通用,但在微服務(wù)項目中,不用Session也不用Cookie,所以想要在微服務(wù)項目中實現(xiàn)會話跟蹤,是有一定難度的。
當前微服務(wù)項目中,身份識別的主流方法是前端將用戶令牌存儲到請求頭中,每次請求將請求頭中的令牌攜帶到后臺,后臺每次從請求頭中獲取令牌來識別用戶身份。
我們在項目操作過程中,很多地方都會用到用戶身份信息,比如下訂單的時候,要知道當前訂單屬于哪個用戶,記錄下單關(guān)鍵日志的時候,需要記錄用戶操作的信息以及用戶信息,關(guān)鍵日志記錄我們一般用AOP進行攔截操作,此時沒法直接把用戶身份信息傳給AOP。這個時候我們可以利用享元模式實現(xiàn)用戶會話信息共享操作。操作流程如下圖:

2.1.2 會話共享案例實現(xiàn)
基于上面的分析,我們采用享元模式實現(xiàn)用戶會話共享操作,要解決如下幾個問題:

定義共享組件:LogComponent
LogComponent 里面定義了每個線程中不變的用戶身份信息 username 、 role 、 sex ,其他的是可能存在變化的數(shù)據(jù),例如用于做日志記錄的 methodName 、 message 。

享元組件邏輯操作對象:SupplementSource
SupplementSource 該對象主要用于給當前線程填充共享數(shù)據(jù),以及變更訪問方法和訪問信息等信息的邏輯操作,代碼如下:

多線程安全控制:ThreadUserLog
每個線程請求的時候,我們需要保障會話安全,比如A線程訪問和B線程訪問,他們的用戶會話身份不能因為并發(fā)原因而發(fā)生混亂。這里我們可以采用ThreadLocal來實現(xiàn)。我們創(chuàng)建一個 ThreadUserLog 對象,并在該對象中創(chuàng)建ThreadLocal


線程會話初始化:AuthorizationInterceptor
AuthorizationInterceptor 攔截器的作用是用于初始化用戶訪問的時候用戶的身份信息,并將身份信息存儲到ThreadUserLog 的 ThreadLocal 中,在用戶訪問方法結(jié)束,銷毀 ThreadUserLog 的 ThreadLocal 中會話,代碼如下:


創(chuàng)建 MvcConfig 來配置攔截器 AuthorizationInterceptor ,代碼如下:
共享信息使用:
①AOP記錄日志:創(chuàng)建AOP切面類 LogAspect 用于記錄日志,代碼如下:

②添加訂單獲取用戶信息:在添加訂單方法 OrderServiceImpl.add(Order order) 中,從ThreadUserLog中獲取用戶會話,并填充給Order,代碼如下

添加訂單,日志輸出可以看到調(diào)用添加訂單和修改庫存時,都記錄了日志,并且獲取了用戶會話,效果如下:

加的訂單數(shù)據(jù)庫數(shù)據(jù)中也擁有用戶信息,效果如下:

3 裝飾著模式
定義:動態(tài)的向一個現(xiàn)有的對象添加新的功能,同時又不改變其結(jié)構(gòu)。它屬于結(jié)構(gòu)型模式。
優(yōu)點:裝飾類和被裝飾類可以獨立發(fā)展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態(tài)擴展一個實現(xiàn)類的功能
缺點:多層裝飾比較復雜。
3.1 裝飾者模式實戰(zhàn)
案例:結(jié)算價格計算,根據(jù)不同價格嵌套運算
在訂單提交的時候,訂單價格和結(jié)算價格其實是兩碼事,訂單價格是當前商品成交價格,而結(jié)算價格是用戶最終需要支付的金額,最終支付的金額并不是一成不變,它也并不是商品成交價格,能改變結(jié)算價格的因素很多,比如滿100減10元,VIP用戶再減5塊。訂單結(jié)算金額計算我們就可以采用裝飾者模式。

3.1.2 裝飾者模式價格運算實現(xiàn)
實現(xiàn)思路分析:

基礎(chǔ)接口:創(chuàng)建接口 MoneySum ,該接口只用于定義計算訂單金額的方法。

訂單金額計算類:創(chuàng)建類 OrderPayMoneyOperation 實現(xiàn)訂單金額的計算。

裝飾者類:創(chuàng)建裝飾者類 DecoratorMoneySum 供其他類擴展。
滿100減10元價格計算:創(chuàng)建類 FullMoneySum 擴展裝飾者類,實現(xiàn)滿減價格計算。
VIP優(yōu)惠10元價格計算:創(chuàng)建類 VipMoneySum ,實現(xiàn)VIP優(yōu)惠計算。

支付金額計算:修改 OrderServiceImpl 的 add() 方法,添加訂單金額以及訂單支付金額的計算功能,代碼如下:

測試效果:
測試數(shù)據(jù)中,我們選擇購買1件商品,當前登錄用戶為王五,擁有5個金幣,當前購買的商品id=1,商品單價是150元,滿減100,VIP優(yōu)惠5元,最終支付135元。

測試生成的訂單如下:

不僅如此,我們可以隨時撤掉滿減和Vip優(yōu)惠功能。
4 策略模式
定義:策略模式是對算法的包裝,把使用算法的責任和算法本身分隔開,委派給不同的對象管理。策略模式通常把一系列的算法包裝到一系列的策略類里面,作為一個抽象策略類的子類。
簡單來說就是就定義一個策略接口,子類策略去實現(xiàn)該接口去定義不同的策略。然后定義一個環(huán)境(Context,也就是需要用到策略的對象)類,以策略接口作為成員變量,根據(jù)環(huán)境來使用具體的策略。
優(yōu)點:

缺點:

4.1 策略模式實戰(zhàn)
案例:結(jié)算價格計算,根據(jù)Vip不同等級進行運算
4.1.1 不同VIP優(yōu)惠價格分析

用戶在購買商品的時候,很多時候會根據(jù)Vip等級打不同折扣,尤其是在線商城中體現(xiàn)的淋漓盡致。我們這里也基于真實電商案例來實現(xiàn)VIP等級價格制:

4.1.2 代碼實現(xiàn)
定義策略接口:Strategy

定義Vip0策略:StrategyVipOne

定義Vip1策略:?StrategyVipTwo

定義Vip2策略:StrategyVipThree

定義Vip3策略:StrategyVipFour

定義策略工廠:StrategyFactory

等級策略配置:修改application.yml,將如下策略配置進去

等級控制:修改 UserHandler 添加等級屬性

修改 UserHandlerShare 定義等級,代碼如下:

裝飾者模式中修改 VipMoneySum 的價格運算,代碼如下:

測試:

5 工廠模式
案例:收銀案例,根據(jù)不同支付方式,選擇不同支付渠道
定義:定義一個創(chuàng)建產(chǎn)品對象的工廠接口,將產(chǎn)品對象的實際創(chuàng)建工作推遲到具體子工廠類當中。這滿足創(chuàng)建型模式中所要求的“創(chuàng)建與使用相分離”的特點。
5.1 工廠模式實戰(zhàn)
5.1.1 支付渠道選中分析
用戶每次下單完成后,需要支付訂單,支付訂單會根據(jù)自身情況選擇不同支付方式,后臺服務(wù)會根據(jù)用戶選中不同創(chuàng)建不同支付渠道的實例,這里創(chuàng)建支付渠道的實例可以采用工廠方法模式。

5.1.2 代碼實現(xiàn)
工廠方法在SpringBoot中如果在用工廠的同時又出現(xiàn)了new,那絕對是一個敗筆。在SpringBoot中,幾乎所有對象
都可以直接交給Spring容器管理,它天然已經(jīng)是一個工廠對象,因此在SpringBoot項目中用工廠模式,再頻繁new對象反而不妥。那么工廠模式究竟怎么用在SpringBoot項目的支付場景呢?其實很簡單,如果我們要選擇一個支付渠道,而項目中有100種支付渠道,那這個時候無疑要寫100個 @Autowired 注入100個支付渠道,并且需要做100種判斷,這個時候我們可以用工廠模式取代我們判斷,直接根據(jù)我們配置的映射關(guān)系從Spring容器中拿到對應(yīng)支付渠道的實例,代碼量將大大減少,這塊是使用工廠的另一種妙用
支付接口:?PayChannel 定義支付行為。

微信支付實現(xiàn):WeixinPay 實現(xiàn)微信支付操作,這里只模擬。

支付寶支付實現(xiàn):?AliPay 實現(xiàn)支付寶支付,這里只模擬。

支付渠道映射配置:在 application.yml 中配置支付渠道映射,每次從前端傳入支付ID即可從配置中獲取支付渠道對應(yīng)Spring容器中實例的id。

支付渠道獲取工廠創(chuàng)建:創(chuàng)建 PayFactory 用于獲取支付渠道的實例,我們這里通過映射的key獲取Spring容器中實例的id值,然后從Spring容器中根據(jù)id獲取對應(yīng)實例,因此該工廠需要實現(xiàn)接口 ApplicationContextAware 來獲取容器。

支付渠道調(diào)用實現(xiàn):修改 PayServiceImpl 的 pay 方法,實現(xiàn)支付,代碼如下:

測試:當我們選中支付渠道為1(微信支付)或者2(支付寶支付)的時候,都能爭取獲取對應(yīng)的渠道實例。

6 狀態(tài)模式
案例:訂單不同狀態(tài),執(zhí)行不同操作
定義:對有狀態(tài)的對象,把復雜的“判斷邏輯”提取到不同的狀態(tài)對象中,允許狀態(tài)對象在其內(nèi)部狀態(tài)發(fā)生改變時改變其行為。
優(yōu)點

缺點

6.1 狀態(tài)模式實戰(zhàn)
6.1.1 狀態(tài)模式案例分析
在電商案例中,訂單狀態(tài)每次發(fā)生變更,都要執(zhí)行不同的操作,這里正好可以使用狀態(tài)模式。當訂單完成支付的時候,我們需要立即通知商家發(fā)貨,當訂單執(zhí)行取消的時候,我們需要執(zhí)行庫存回滾,如果訂單已支付,還需要執(zhí)行退款操作,無論是通知商家發(fā)貨還是執(zhí)行庫存回滾,都是有訂單狀態(tài)決定,因此這里可以使用狀態(tài)模式來實現(xiàn)
我們可以先定義狀態(tài)接口 State ,給狀態(tài)接口實現(xiàn)兩個不同的行為,分別是發(fā)貨和回滾庫存并退款,把該狀態(tài)對象添加到訂單內(nèi)部作為成員屬性,當訂單的 state 狀態(tài)改變時,觸發(fā)執(zhí)行不同的狀態(tài)行為動作。

5.1.2 案例實現(xiàn)
狀態(tài)接口定義:State 接口,用于定義更新狀態(tài)對象,同時執(zhí)行相關(guān)的行為。

發(fā)通知消息行為定義:SendMsgBehavior 用于實現(xiàn)給商家發(fā)送消息通知發(fā)貨,這里模擬發(fā)送消息的行為。

庫存回滾并退款:創(chuàng)建 ResetStoreBehavior ,用于實現(xiàn)訂單庫存回滾,并給用戶退款操作,這里退款模擬相關(guān)行
為。

測試:
支付訂單的時候,如果支付成功,我們調(diào)用 State 變更對應(yīng)的狀態(tài)行為,并執(zhí)行相關(guān)行為,代碼如下:

測試結(jié)果如下:

——————END—————— 歡迎關(guān)注“Java引導者”,我們分享最有價值的Java的干貨文章,助力您成為有思想的Java開發(fā)工程師!
