設計模式學習(1)

? ??

????之前老是對設計模式一知半解,單獨看每一行代碼都能看得懂,發現別人設計的結構都非常清晰,一輪到自己,就會發現除了寫的爽,毫無優點。

????有幸拜讀了一篇博客——《設計模式二三事》,讀完之后發現文章中對于設計模式的講解很有意思,結合示例進行演示。跟著操作一遍發現自己對設計模式理解更清楚了些,所以本次小韓也是跟著學習敲了一遍,并把過程進行了復述。如果想了解更多的歡迎訪問 原文地址:

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>


?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容