? ??
????之前老是對設計模式一知半解,單獨看每一行代碼都能看得懂,發現別人設計的結構都非常清晰,一輪到自己,就會發現除了寫的爽,毫無優點。
????有幸拜讀了一篇博客——《設計模式二三事》,讀完之后發現文章中對于設計模式的講解很有意思,結合示例進行演示。跟著操作一遍發現自己對設計模式理解更清楚了些,所以本次小韓也是跟著學習敲了一遍,并把過程進行了復述。如果想了解更多的歡迎訪問 原文地址:
https://tech.meituan.com/2022/03/10/interesting-talk-about-design-patterns.html
那么如何運用好一個設計模式,或者說設計一個非常漂亮的結構呢?接下來,我們借用一些示例分享一些設計模式的使用參考:
????背景示例:
????我們假設現在就要做一個營銷,需要用戶參與一個活動,然后完成一系列的任務,最后可以得到一些獎勵作為回報?;顒拥莫剟畎缊F外賣、酒旅和美食等多種品類券,現在需要你幫忙設計一套獎勵發放方案。
????一般我們寫法是:
????????/** ?*?背景: ?*?我們假設現在就要做一個營銷, ?*?需要用戶參與一個活動, ?*?然后完成一系列的任務,最后可以得到一些獎勵作為回報。 ?*?活動的獎勵包含美團外賣、酒旅和美食等多種品類券, ?*?現在需要你幫忙設計一套獎勵發放方案。 ?* ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ public?class?RewardService?{ ????//?外部服務 ????private?WaimaiService?waimaiService; ????private?HotelService?hotelService; ????private?FoodService?foodService; ????//?使用對入參的條件判斷進行發獎 ????public?void?issueReward(String?rewardType,?Object...?params)?{ ????????if?("Waimai".equals(rewardType))?{ ????????????WaimaiRequest?request?=?new?WaimaiRequest(); ????????????//?構建入參 ????????????request.setWaimaiReq(params); ????????????waimaiService.issueWaimai(request); ????????}?else?if?("Hotel".equals(rewardType))?{ ????????????HotelRequest?request?=?new?HotelRequest(); ????????????request.setHotelReq(params); ????????????hotelService.sendPrize(request); ????????}?else?if?("Food".equals(rewardType))?{ ????????????FoodRequest?request?=?new?FoodRequest(); ????????????request.setFoodReq(params); ????????????foodService.getCoupon(request); ????????}?else?{ ????????????throw?new?IllegalArgumentException("rewardType?error!"); ????????} ????} }
????可以看到根據背景要求,上面的代碼是小韓通常的寫法。但是我們也會發現,如果已有模式發生變更,或者我們新增加一種模式,就必須修改上述的代碼。
????一是不符合開閉原則,可以預見,如果后續新增品類券的話,需要直接修改主干代碼,而我們提倡代碼應該是對修改封閉的;
????二是不符合迪米特法則,發獎邏輯和各個下游接口高度耦合,這導致接口的改變將直接影響到代碼的組織,使得代碼的可維護性降低。
????
????那么如何優化呢?這個時候我們可以了解下策略模式和適配器模式來優化
????策略模式
????定義了一系列的算法,并將每一個算法封裝起來,使它們可以相互替換。
策略模式通常包含以下角色:
抽象策略(Strategy)類:定義了一個公共接口,各種不同的算法以不同的方式實現這個接口,環境角色使用這個接口調用不同的算法,一般使用接口或抽象類實現。
具體策略(Concrete Strategy)類:實現了抽象策略定義的接口,提供具體的算法實現。
環境(Context)類:持有一個策略類的引用,最終給客戶端調用。
????適配器模式
????將一個類的接口轉換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的那些類能一起工作。
適配器模式包含以下主要角色:
目標(Target)接口:當前系統業務所期待的接口,它可以是抽象類或接口。
適配者(Adaptee)類:它是被訪問和適配的現存組件庫中的組件接口。
適配器(Adapter)類:它是一個轉換器,通過繼承或引用適配者的對象,把適配者接口轉換成目標接口,讓客戶按目????標接口的格式訪問適配者。
那么我們來實現下(好記性不如爛筆頭)
代碼結構:
第一步:我們定義了一個接口
/** ?*?定義接口:策略接口 ?* ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ public?interface?Strategy?{ ????//?策略實現方法 ????void?issue(Object...?params); }
第二步:讓之前的三個策略都實現定義的Strategy接口
/** ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ public?class?FoodStrategy?implements?Strategy?{ ????private?FoodService?foodService; ????@Override ????public?void?issue(Object...?params)?{ ????????FoodRequest?foodRequest?=?new?FoodRequest(); ????????foodRequest.setFoodReq(params); ????????foodService.getCoupon(foodRequest); ????} } /** ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ public?class?HotelStrategy?implements?Strategy?{ ????private?HotelService?hotelService; ????@Override ????public?void?issue(Object...?params)?{ ????????HotelRequest?hotelRequest?=?new?HotelRequest(); ????????hotelService.sendPrize(hotelRequest); ????} } /** ?*?@author?程序原小韓 ?*?@date?2022/3/19 ?*/ public?class?WaimaiStrategy?implements?Strategy?{ ????private?WaimaiService?waimaiService; ????@Override ????public?void?issue(Object...?params)?{ ????????WaimaiRequest?request?=?new?WaimaiRequest(); ????????//?構建入參 ????????request.setWaimaiReq(params); ????????waimaiService.issueWaimai(request); ????} }
第三步:定義策略類(我喜歡叫它路由類)
/** ?*??定義策略類,其實我更新換理解為路由類 ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ public?class?StrategyContext?{ ????public?static?Strategy?getStrategy(String?rewardType)?{ ????????switch?(rewardType)?{ ????????????case?"Waimai": ????????????????return?new?WaimaiStrategy(); ????????????case?"Hotel": ????????????????return?new?HotelStrategy(); ????????????case?"Food": ????????????????return?new?FoodStrategy(); ????????????default: ????????????????throw?new?IllegalArgumentException("rewardType?error!"); ????????} ????} }
第四步:看我們如何調用
/** ?*?背景: ?*?我們假設現在就要做一個營銷, ?*?需要用戶參與一個活動, ?*?然后完成一系列的任務,最后可以得到一些獎勵作為回報。 ?*?活動的獎勵包含美團外賣、酒旅和美食等多種品類券, ?*?現在需要你幫忙設計一套獎勵發放方案。 ?* ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ public?class?RewardService?{ ????//?使用對入參的條件判斷進行發獎 ????public?void?issueReward(String?rewardType,?Object...?params)?{ ????????Strategy?strategy?=?StrategyContext.getStrategy(rewardType); ????????strategy.issue(params); ????} }
通過上面一通操作,忽然會發現,代碼變得“高大上”了起來誒!而且動態擴展或者修改都變的方便了許多;各個策略之間都存在隔離(物理),對比第一次的代碼編寫,本次顯而易見,牛x了很多。????
????但是,對于策略對象每次我們都要新創建一個,有必要么?而且環境類的獲取策略方法職責很明確,但是你依然沒有做到完全對修改封閉。
????那么是不是可以將策略類單例化以減少開銷,并實現自注冊的功能徹底解決分支判斷。
????單例模式
?????設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
????我們根據上面發現的問題再進行下修改,結構如下:
第一步:修改策略上下文類,使其具備注冊與獲取策略的功能
/** ?*?定義策略類,其實我更新換理解為路由類 ?*??策略上下文,用于管理策略的注冊和獲取 ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ public?class?StrategyContext?{ ????//?緩存策略類 ????private?static?final?HashMap<String,?Strategy>?strategyRegisterCache?=?new?HashMap(); ????/** ?????*?注冊策略 ?????*?@param?rewardType??類型 ?????*?@param?strategy?實例 ?????*/ ????public?static?void?registerStrategy(String?rewardType,?Strategy?strategy)?{ ????????strategyRegisterCache.put(rewardType,strategy); ????} ????/** ?????*?獲取策略 ?????*?@param?rewardType?????*?@return?????*/ ????public?static?Strategy?getStrategy(String?rewardType)?{ ????????return??strategyRegisterCache.get(rewardType); ????} }
第二步,創建個抽象類,定義注冊方法
/** ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ abstract?class?AbstractStrategy?implements?Strategy?{ ????//?類注冊方法 ????public?void?register()?{ ????????StrategyContext.registerStrategy(getClass().getSimpleName(),?this); ????} }
第三步,修改策略類,使其繼承我們定義的策略抽象類,可以完成自動注冊
/** ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ public?class?Food?extends?AbstractStrategy?{ ????private?static?final?Food?instance?=?new?Food(); ????private?FoodService?foodService; ????private?Food()?{ ????????register(); ????} ????public?static?Food?getInstance()?{ ????????return?instance; ????} ????@Override ????public?void?issue(Object...?params)?{ ????????FoodRequest?foodRequest?=?new?FoodRequest(); ????????foodRequest.setFoodReq(params); ????????foodService.getCoupon(foodRequest); ????} } /** ?*?@author?程序員小韓 ?*?@date?2022/3/19 ?*/ public?class?Hotel?extends?AbstractStrategy?{ ????private?static?final?Hotel?instance?=?new?Hotel(); ????private?HotelService?hotelService; ????private?Hotel()?{ ????????register(); ????} ????public?static?Hotel?getInstance()?{ ????????return?instance; ????} ????@Override ????public?void?issue(Object...?params)?{ ????????HotelRequest?hotelRequest?=?new?HotelRequest(); ????????hotelService.sendPrize(hotelRequest); ????} } /** ?*?@author?程序原小韓 ?*?@date?2022/3/19 ?*/ public?class?Waimai?extends?AbstractStrategy?{ ????private?static?final?Waimai?instance?=?new?Waimai(); ????private?WaimaiService?waimaiService; ????private?Waimai()?{ ????????register(); ????} ????public?static?Waimai?getInstance()?{ ????????return?instance; ????} ????@Override ????public?void?issue(Object...?params)?{ ????????WaimaiRequest?request?=?new?WaimaiRequest(); ????????//?構建入參 ????????request.setWaimaiReq(params); ????????waimaiService.issueWaimai(request); ????} }
RewardService.java 無變化,就不在粘貼代碼。
????好了到此我們的代碼已經優化到很滿意的程度了。再優化的過程中我們使用到了?策略模式、適配器模式和單例模式,你學廢了么?
…………………………………分割線……………………………
不積跬步,無以至千里;不積小流,無以成江海。
關注我,每天分享一些小知識點。分享自己的小心得,包含但不限于初、中、高級面試題呦!??!
我都墨跡這么半天了 ,你不點關注,不點贊,不收藏,還不轉發,你想干啥!?。。?/p>