<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          實戰(zhàn)!工作中常用到哪些設(shè)計模式

          共 9422字,需瀏覽 19分鐘

           ·

          2021-11-23 15:32

          前言

          平時我們寫代碼呢,多數(shù)情況都是流水線式寫代碼,基本就可以實現(xiàn)業(yè)務(wù)邏輯了。如何在寫代碼中找到樂趣呢,我覺得,最好的方式就是:使用設(shè)計模式優(yōu)化自己的業(yè)務(wù)代碼。今天跟大家聊聊日常工作中,我都使用過哪些設(shè)計模式。

          工作中常用到哪些設(shè)計模式

          1.策略模式

          1.1 業(yè)務(wù)場景

          假設(shè)有這樣的業(yè)務(wù)場景,大數(shù)據(jù)系統(tǒng)把文件推送過來,根據(jù)不同類型采取不同的解析方式。多數(shù)的小伙伴就會寫出以下的代碼:

          if(type=="A"){
          ???//按照A格式解析
          ?
          }else?if(type=="B"){
          ????//按B格式解析
          }else{
          ????//按照默認(rèn)格式解析
          }

          這個代碼可能會存在哪些問題呢?

          • 如果分支變多,這里的代碼就會變得臃腫,難以維護,可讀性低
          • 如果你需要接入一種新的解析類型,那只能在原有代碼上修改。

          說得專業(yè)一點的話,就是以上代碼,違背了面向?qū)ο缶幊痰?strong>開閉原則以及單一原則

          • 開閉原則(對于擴展是開放的,但是對于修改是封閉的):增加或者刪除某個邏輯,都需要修改到原來代碼
          • 單一原則(規(guī)定一個類應(yīng)該只有一個發(fā)生變化的原因):修改任何類型的分支邏輯代碼,都需要改動當(dāng)前類的代碼。

          如果你的代碼就是醬紫:有多個if...else等條件分支,并且每個條件分支,可以封裝起來替換的,我們就可以使用策略模式來優(yōu)化。

          1.2 策略模式定義

          策略模式定義了算法族,分別封裝起來,讓它們之間可以相互替換,此模式讓算法的變化獨立于使用算法的的客戶。這個策略模式的定義是不是有點抽象呢?那我們來看點通俗易懂的比喻:

          假設(shè)你跟不同性格類型的小姐姐約會,要用不同的策略,有的請電影比較好,有的則去吃小吃效果不錯,有的去逛街買買買最合適。當(dāng)然,目的都是為了得到小姐姐的芳心,請看電影、吃小吃、逛街就是不同的策略。

          策略模式針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。

          1.3 策略模式使用

          策略模式怎么使用呢?醬紫實現(xiàn)的:

          • 一個接口或者抽象類,里面兩個方法(一個方法匹配類型,一個可替換的邏輯實現(xiàn)方法)
          • 不同策略的差異化實現(xiàn)(就是說,不同策略的實現(xiàn)類)
          • 使用策略模式

          1.3.1 一個接口,兩個方法

          public?interface?IFileStrategy?{
          ????
          ????//屬于哪種文件解析類型
          ????FileTypeResolveEnum?gainFileType();
          ????
          ????//封裝的公用算法(具體的解析方法)
          ????void?resolve(Object?objectparam);
          }

          1.3.2 不同策略的差異化實現(xiàn)

          A 類型策略具體實現(xiàn)

          @Component
          public?class?AFileResolve?implements?IFileStrategy?{
          ????
          ????@Override
          ????public?FileTypeResolveEnum?gainFileType()?{
          ????????return?FileTypeResolveEnum.File_A_RESOLVE;
          ????}

          ????@Override
          ????public?void?resolve(Object?objectparam)?{
          ??????logger.info("A 類型解析文件,參數(shù):{}",objectparam);
          ??????//A類型解析具體邏輯
          ????}
          }

          B 類型策略具體實現(xiàn)

          @Component
          public?class?BFileResolve?implements?IFileStrategy?{
          ???
          ????@Override
          ????public?FileTypeResolveEnum?gainFileType()?{
          ????????return?FileTypeResolveEnum.File_B_RESOLVE;
          ????}


          ????@Override
          ????public?void?resolve(Object?objectparam)?{
          ??????logger.info("B 類型解析文件,參數(shù):{}",objectparam);
          ??????//B類型解析具體邏輯
          ????}
          }

          默認(rèn)類型策略具體實現(xiàn)

          @Component
          public?class?DefaultFileResolve?implements?IFileStrategy?{

          ????@Override
          ????public?FileTypeResolveEnum?gainFileType()?{
          ????????return?FileTypeResolveEnum.File_DEFAULT_RESOLVE;
          ????}

          ????@Override
          ????public?void?resolve(Object?objectparam)?{
          ??????logger.info("默認(rèn)類型解析文件,參數(shù):{}",objectparam);
          ??????//默認(rèn)類型解析具體邏輯
          ????}
          }

          1.3.3 使用策略模式

          如何使用呢?我們借助spring的生命周期,使用ApplicationContextAware接口,把對用的策略,初始化到map里面。然后對外提供resolveFile方法即可。

          /**
          ?*??@author 公眾號:撿田螺的小男孩
          ?*/
          @Component
          public?class?StrategyUseService?implements?ApplicationContextAware{

          ??
          ????private?Map?iFileStrategyMap?=?new?ConcurrentHashMap<>();

          ????public?void?resolveFile(FileTypeResolveEnum?fileTypeResolveEnum,?Object?objectParam)?{
          ????????IFileStrategy?iFileStrategy?=?iFileStrategyMap.get(fileTypeResolveEnum);
          ????????if?(iFileStrategy?!=?null)?{
          ????????????iFileStrategy.resolve(objectParam);
          ????????}
          ????}

          ????//把不同策略放到map
          ????@Override
          ????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
          ????????Map?tmepMap?=?applicationContext.getBeansOfType(IFileStrategy.class);
          ????????tmepMap.values().forEach(strategyService?->?iFileStrategyMap.put(strategyService.gainFileType(),?strategyService));
          ????}
          }

          2. 責(zé)任鏈模式

          2.1 業(yè)務(wù)場景

          我們來看一個常見的業(yè)務(wù)場景,下訂單。下訂單接口,基本的邏輯,一般有參數(shù)非空校驗、安全校驗、黑名單校驗、規(guī)則攔截等等。很多伙伴會使用異常來實現(xiàn):

          public?class?Order?{

          ????public?void?checkNullParam(Object?param){
          ??????//參數(shù)非空校驗
          ??????throw?new?RuntimeException();
          ????}
          ????public?void?checkSecurity(){
          ??????//安全校驗
          ??????throw?new?RuntimeException();
          ????}
          ????public?void?checkBackList(){
          ????????//黑名單校驗
          ????????throw?new?RuntimeException();
          ????}
          ????public?void?checkRule(){
          ????????//規(guī)則攔截
          ????????throw?new?RuntimeException();
          ????}

          ????public?static?void?main(String[]?args)?{
          ????????Order?order=?new?Order();
          ????????try{
          ????????????order.checkNullParam();
          ????????????order.checkSecurity?();
          ????????????order.checkBackList();
          ????????????order2.checkRule();
          ????????????System.out.println("order?success");
          ????????}catch?(RuntimeException?e){
          ????????????System.out.println("order?fail");
          ????????}
          ????}
          }

          這段代碼使用了異常來做邏輯條件判斷,如果后續(xù)邏輯越來越復(fù)雜的話,會出現(xiàn)一些問題:如異常只能返回異常信息,不能返回更多的字段,這時候需要自定義異常類。

          并且,阿里開發(fā)手冊規(guī)定:禁止用異常做邏輯判斷。

          【強制】 異常不要用來做流程控制,條件控制。說明:異常設(shè)計的初衷是解決程序運行中的各種意外情況,且異常的處理效率比條件判斷方式要低很多。

          如何優(yōu)化這段代碼呢?可以考慮責(zé)任鏈模式

          2.2 責(zé)任鏈模式定義

          當(dāng)你想要讓一個以上的對象有機會能夠處理某個請求的時候,就使用責(zé)任鏈模式

          責(zé)任鏈模式為請求創(chuàng)建了一個接收者對象的鏈。執(zhí)行鏈上有多個對象節(jié)點,每個對象節(jié)點都有機會(條件匹配)處理請求事務(wù),如果某個對象節(jié)點處理完了,就可以根據(jù)實際業(yè)務(wù)需求傳遞給下一個節(jié)點繼續(xù)處理或者返回處理完畢。這種模式給予請求的類型,對請求的發(fā)送者和接收者進行解耦。

          責(zé)任鏈模式實際上是一種處理請求的模式,它讓多個處理器(對象節(jié)點)都有機會處理該請求,直到其中某個處理成功為止。責(zé)任鏈模式把多個處理器串成鏈,然后讓請求在鏈上傳遞:

          責(zé)任鏈模式

          打個比喻:

          假設(shè)你晚上去上選修課,為了可以走點走,坐到了最后一排。來到教室,發(fā)現(xiàn)前面坐了好幾個漂亮的小姐姐,于是你找張紙條,寫上:“你好, 可以做我的女朋友嗎?如果不愿意請向前傳”。紙條就一個接一個的傳上去了,后來傳到第一排的那個妹子手上,她把紙條交給老師,聽說老師40多歲未婚...

          2.3 責(zé)任鏈模式使用

          責(zé)任鏈模式怎么使用呢?

          • 一個接口或者抽象類
          • 每個對象差異化處理
          • 對象鏈(數(shù)組)初始化(連起來)

          2.3.1 一個接口或者抽象類

          這個接口或者抽象類,需要:

          • 有一個指向責(zé)任下一個對象的屬性
          • 一個設(shè)置下一個對象的set方法
          • 給子類對象差異化實現(xiàn)的方法(如以下代碼的doFilter方法)
          /**
          ?*??關(guān)注公眾號:撿田螺的小男孩
          ?*/
          public?abstract?class?AbstractHandler?{

          ????//責(zé)任鏈中的下一個對象
          ????private?AbstractHandler?nextHandler;

          ????/**
          ?????*?責(zé)任鏈的下一個對象
          ?????*/
          ????public?void?setNextHandler(AbstractHandler?nextHandler){
          ????????this.nextHandler?=?nextHandler;
          ????}

          ????/**
          ?????*?具體參數(shù)攔截邏輯,給子類去實現(xiàn)
          ?????*/
          ????public?void?filter(Request?request,?Response?response)?{
          ????????doFilter(request,?response);
          ????????if?(getNextHandler()?!=?null)?{
          ????????????getNextHandler().filter(request,?response);
          ????????}
          ????}

          ????public?AbstractHandler?getNextHandler()?{
          ????????return?nextHandler;
          ????}

          ?????abstract?void?doFilter(Request?filterRequest,?Response?response);

          }

          2.3.2 每個對象差異化處理

          責(zé)任鏈上,每個對象的差異化處理,如本小節(jié)的業(yè)務(wù)場景,就有參數(shù)校驗對象、安全校驗對象、黑名單校驗對象、規(guī)則攔截對象

          /**
          ?*?參數(shù)校驗對象
          ?**/
          @Component
          @Order(1)?//順序排第1,最先校驗
          public?class?CheckParamFilterObject?extends?AbstractHandler?{

          ????@Override
          ????public?void?doFilter(Request?request,?Response?response)?{
          ????????System.out.println("非空參數(shù)檢查");
          ????}
          }

          /**
          ?*??安全校驗對象
          ?*/
          @Component
          @Order(2)?//校驗順序排第2
          public?class?CheckSecurityFilterObject?extends?AbstractHandler?{

          ????@Override
          ????public?void?doFilter(Request?request,?Response?response)?{
          ????????//invoke?Security?check
          ????????System.out.println("安全調(diào)用校驗");
          ????}
          }
          /**
          ?*??黑名單校驗對象
          ?*/
          @Component
          @Order(3)?//校驗順序排第3
          public?class?CheckBlackFilterObject?extends?AbstractHandler?{

          ????@Override
          ????public?void?doFilter(Request?request,?Response?response)?{
          ????????//invoke?black?list?check
          ????????System.out.println("校驗黑名單");
          ????}
          }

          /**
          ?*??規(guī)則攔截對象
          ?*/
          @Component
          @Order(4)?//校驗順序排第4
          public?class?CheckRuleFilterObject?extends?AbstractHandler?{

          ????@Override
          ????public?void?doFilter(Request?request,?Response?response)?{
          ????????//check?rule
          ????????System.out.println("check?rule");
          ????}
          }

          2.3.3 對象鏈連起來(初始化)&& 使用

          @Component("ChainPatternDemo")
          public?class?ChainPatternDemo?{

          ????//自動注入各個責(zé)任鏈的對象
          ????@Autowired
          ????private?List?abstractHandleList;

          ????private?AbstractHandler?abstractHandler;

          ????//spring注入后自動執(zhí)行,責(zé)任鏈的對象連接起來
          ????@PostConstruct
          ????public?void?initializeChainFilter(){

          ????????for(int?i?=?0;i????????????if(i?==?0){
          ????????????????abstractHandler?=?abstractHandleList.get(0);
          ????????????}else{
          ????????????????AbstractHandler?currentHander?=?abstractHandleList.get(i?-?1);
          ????????????????AbstractHandler?nextHander?=?abstractHandleList.get(i);
          ????????????????currentHander.setNextHandler(nextHander);
          ????????????}
          ????????}
          ????}

          ????//直接調(diào)用這個方法使用
          ????public?Response?exec(Request?request,?Response?response)?{
          ????????abstractHandler.filter(request,?response);
          ????????return?response;
          ????}

          ????public?AbstractHandler?getAbstractHandler()?{
          ????????return?abstractHandler;
          ????}

          ????public?void?setAbstractHandler(AbstractHandler?abstractHandler)?{
          ????????this.abstractHandler?=?abstractHandler;
          ????}
          }

          運行結(jié)果如下:

          非空參數(shù)檢查
          安全調(diào)用校驗
          校驗黑名單
          check?rule

          3. 模板方法模式

          3.1 業(yè)務(wù)場景

          假設(shè)我們有這么一個業(yè)務(wù)場景:內(nèi)部系統(tǒng)不同商戶,調(diào)用我們系統(tǒng)接口,去跟外部第三方系統(tǒng)交互(http方式)。走類似這么一個流程,如下:

          一個請求都會經(jīng)歷這幾個流程:

          • 查詢商戶信息
          • 對請求報文加簽
          • 發(fā)送http請求出去
          • 對返回的報文驗簽

          這里,有的商戶可能是走代理出去的,有的是走直連。假設(shè)當(dāng)前有A,B商戶接入,不少伙伴可能這么實現(xiàn),偽代碼如下:


          //?商戶A處理句柄
          CompanyAHandler?implements?RequestHandler?{
          ???Resp?hander(req){
          ???//查詢商戶信息
          ???queryMerchantInfo();
          ???//加簽
          ???signature();
          ???//http請求(A商戶假設(shè)走的是代理)
          ???httpRequestbyProxy()
          ???//驗簽
          ???verify();
          ???}
          }
          //?商戶B處理句柄
          CompanyBHandler?implements?RequestHandler?{
          ???Resp?hander(Rreq){
          ???//查詢商戶信息
          ???queryMerchantInfo();
          ???//加簽
          ???signature();
          ???//?http請求(B商戶不走代理,直連)
          ???httpRequestbyDirect();
          ???//?驗簽
          ???verify();?
          ???}
          }

          假設(shè)新加一個C商戶接入,你需要再實現(xiàn)一套這樣的代碼。顯然,這樣代碼就重復(fù)了,一些通用的方法,卻在每一個子類都重新寫了這一方法。

          如何優(yōu)化呢?可以使用模板方法模式。

          3.2 模板方法模式定義

          定義一個操作中的算法的骨架流程,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。它的核心思想就是:定義一個操作的一系列步驟,對于某些暫時確定不下來的步驟,就留給子類去實現(xiàn),這樣不同的子類就可以定義出不同的步驟。

          打個通俗的比喻:

          模式舉例:追女朋友要先“牽手”,再“擁抱”,再“接吻”, 再“拍拍..額..手”。至于具體你用左手還是右手牽,無所謂,但是整個過程,定了一個流程模板,按照模板來就行。

          3.3 模板方法使用

          • 一個抽象類,定義骨架流程(抽象方法放一起)
          • 確定的共同方法步驟,放到抽象類(去除抽象方法標(biāo)記)
          • 不確定的步驟,給子類去差異化實現(xiàn)

          我們繼續(xù)那以上的舉例的業(yè)務(wù)流程例子,來一起用 模板方法優(yōu)化一下哈:

          3.3.1 一個抽象類,定義骨架流程

          因為一個個請求經(jīng)過的流程為一下步驟:

          • 查詢商戶信息
          • 對請求報文加簽
          • 發(fā)送http請求出去
          • 對返回的報文驗簽

          所以我們就可以定義一個抽象類,包含請求流程的幾個方法,方法首先都定義為抽象方法哈:

          /**
          ?*?抽象類定義骨架流程(查詢商戶信息,加簽,http請求,驗簽)
          ?*/
          abstract?class?AbstractMerchantService??{?

          ??????//查詢商戶信息
          ??????abstract?queryMerchantInfo();
          ??????//加簽
          ??????abstract?signature();
          ??????//http?請求
          ??????abstract?httpRequest();
          ???????//?驗簽
          ???????abstract?verifySinature();
          ?
          }

          3.3.2 確定的共同方法步驟,放到抽象類

          abstract?class?AbstractMerchantService??{?

          ?????//模板方法流程
          ?????Resp?handlerTempPlate(req){
          ???????????//查詢商戶信息
          ???????????queryMerchantInfo();
          ???????????//加簽
          ???????????signature();
          ???????????//http?請求
          ???????????httpRequest();
          ???????????//?驗簽
          ???????????verifySinature();
          ?????}
          ??????//?Http是否走代理(提供給子類實現(xiàn))
          ??????abstract?boolean?isRequestByProxy();
          }

          3.3.3 不確定的步驟,給子類去差異化實現(xiàn)

          因為是否走代理流程是不確定的,所以給子類去實現(xiàn)。

          商戶A的請求實現(xiàn):

          CompanyAServiceImpl?extends?AbstractMerchantService{
          ????Resp?hander(req){
          ??????return?handlerTempPlate(req);
          ????}
          ????//走http代理的
          ????boolean?isRequestByProxy(){
          ???????return?true;
          ????}

          商戶B的請求實現(xiàn):


          CompanyBServiceImpl?extends?AbstractMerchantService{
          ????Resp?hander(req){
          ??????return?handlerTempPlate(req);
          ????}
          ????//公司B是不走代理的
          ????boolean?isRequestByProxy(){
          ???????return?false;
          ????}

          4. 觀察者模式

          4.1 業(yè)務(wù)場景

          登陸注冊應(yīng)該是最常見的業(yè)務(wù)場景了。就拿注冊來說事,我們經(jīng)常會遇到類似的場景,就是用戶注冊成功后,我們給用戶發(fā)一條消息,又或者發(fā)個郵件等等,因此經(jīng)常有如下的代碼:

          void?register(User?user){
          ??insertRegisterUser(user);
          ??sendIMMessage();
          ? sendEmail();
          }

          這塊代碼會有什么問題呢?如果產(chǎn)品又加需求:現(xiàn)在注冊成功的用戶,再給用戶發(fā)一條短信通知。于是你又得改register方法的代碼了。。。這是不是違反了開閉原則啦。

          void?register(User?user){
          ??insertRegisterUser(user);
          ??sendIMMessage();
          ??sendMobileMessage();
          ? sendEmail();
          }

          并且,如果調(diào)發(fā)短信的接口失敗了,是不是又影響到用戶注冊了?!這時候,是不是得加個異步方法給通知消息才好。。。

          實際上,我們可以使用觀察者模式優(yōu)化。

          4.2 觀察者模式定義

          觀察者模式定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被完成業(yè)務(wù)的更新。

          觀察者模式屬于行為模式,一個對象(被觀察者)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知,進行廣播通知。它的主要成員就是觀察者和被觀察者

          • 被觀察者(Observerable):目標(biāo)對象,狀態(tài)發(fā)生變化時,將通知所有的觀察者。
          • 觀察者(observer):接受被觀察者的狀態(tài)變化通知,執(zhí)行預(yù)先定義的業(yè)務(wù)。

          使用場景: 完成某件事情后,異步通知場景。如,登陸成功,發(fā)個IM消息等等。

          4.3 觀察者模式使用

          觀察者模式實現(xiàn)的話,還是比較簡單的。

          • 一個被觀察者的類Observerable ;
          • 多個觀察者Observer ;
          • 觀察者的差異化實現(xiàn)
          • 經(jīng)典觀察者模式封裝:EventBus實戰(zhàn)

          4.3.1 一個被觀察者的類Observerable 和 多個觀察者Observer

          public?class?Observerable?{
          ???
          ???private?List?observers?
          ??????=?new?ArrayList();
          ???private?int?state;
          ?
          ???public?int?getState()?{
          ??????return?state;
          ???}
          ?
          ???public?void?setState(int?state)?{
          ??????notifyAllObservers();
          ???}
          ?
          ???//添加觀察者
          ???public?void?addServer(Observer?observer){
          ??????observers.add(observer);??????
          ???}
          ???
          ???//移除觀察者
          ???public?void?removeServer(Observer?observer){
          ??????observers.remove(observer);??????
          ???}
          ???//通知
          ???public?void?notifyAllObservers(int?state){
          ??????if(state!=1){
          ??????????System.out.println(“不是通知的狀態(tài)”);
          ?????????return?;
          ??????}
          ???
          ??????for?(Observer?observer?:?observers)?{
          ?????????observer.doEvent();
          ??????}
          ???}??
          }

          4.3.2 觀察者的差異化實現(xiàn)

          ?//觀察者
          ?interface?Observer?{??
          ????void?doEvent();??
          }??
          //Im消息
          IMMessageObserver?implements?Observer{
          ????void?doEvent(){
          ???????System.out.println("發(fā)送IM消息");
          ????}
          }

          //手機短信
          MobileNoObserver?implements?Observer{
          ????void?doEvent(){
          ???????System.out.println("發(fā)送短信消息");
          ????}
          }
          //EmailNo
          EmailObserver?implements?Observer{
          ????void?doEvent(){
          ???????System.out.println("發(fā)送email消息");
          ????}
          }

          4.3.3 EventBus實戰(zhàn)

          自己搞一套觀察者模式的代碼,還是有點小麻煩。實際上,Guava EventBus就封裝好了,它 提供一套基于注解的事件總線,api可以靈活的使用,爽歪歪。

          我們來看下EventBus的實戰(zhàn)代碼哈,首先可以聲明一個EventBusCenter類,它類似于以上被觀察者那種角色Observerable。

          public?class?EventBusCenter?{

          ????private?static?EventBus?eventBus?=?new?EventBus();

          ????private?EventBusCenter()?{
          ????}

          ????public?static?EventBus?getInstance()?{
          ????????return?eventBus;
          ????}
          ?????//添加觀察者
          ????public?static?void?register(Object?obj)?{
          ????????eventBus.register(obj);
          ????}
          ????//移除觀察者
          ????public?static?void?unregister(Object?obj)?{
          ????????eventBus.unregister(obj);
          ????}
          ????//把消息推給觀察者
          ????public?static?void?post(Object?obj)?{
          ????????eventBus.post(obj);
          ????}
          }

          然后再聲明觀察者EventListener

          public?class?EventListener?{

          ????@Subscribe?//加了訂閱,這里標(biāo)記這個方法是事件處理方法??
          ????public?void?handle(NotifyEvent?notifyEvent)?{
          ????????System.out.println("發(fā)送IM消息"?+?notifyEvent.getImNo());
          ????????System.out.println("發(fā)送短信消息"?+?notifyEvent.getMobileNo());
          ????????System.out.println("發(fā)送Email消息"?+?notifyEvent.getEmailNo());
          ????}
          }

          //通知事件類
          public?class?NotifyEvent??{

          ????private?String?mobileNo;

          ????private?String?emailNo;

          ????private?String?imNo;

          ????public?NotifyEvent(String?mobileNo,?String?emailNo,?String?imNo)?{
          ????????this.mobileNo?=?mobileNo;
          ????????this.emailNo?=?emailNo;
          ????????this.imNo?=?imNo;
          ????}
          ?}

          使用demo測試:

          public?class?EventBusDemoTest?{

          ????public?static?void?main(String[]?args)?{

          ????????EventListener?eventListener?=?new?EventListener();
          ????????EventBusCenter.register(eventListener);
          ????????EventBusCenter.post(new?NotifyEvent("13372817283",?"[email protected]",?"666"));
          ????????}
          }

          運行結(jié)果:

          發(fā)送IM消息666
          發(fā)送短信消息13372817283
          發(fā)送Email消息[email protected]

          5. 工廠模式

          5.1 業(yè)務(wù)場景

          工廠模式一般配合策略模式一起使用。用來去優(yōu)化大量的if...else...switch...case...條件語句。

          我們就取第一小節(jié)中策略模式那個例子吧。根據(jù)不同的文件解析類型,創(chuàng)建不同的解析對象

          ?
          ?IFileStrategy?getFileStrategy(FileTypeResolveEnum?fileType){
          ?????IFileStrategy??fileStrategy?;
          ?????if(fileType=FileTypeResolveEnum.File_A_RESOLVE){
          ???????fileStrategy?=?new?AFileResolve();
          ?????}else?if(fileType=FileTypeResolveEnum.File_A_RESOLV){
          ???????fileStrategy?=?new?BFileResolve();
          ?????}else{
          ???????fileStrategy?=?new?DefaultFileResolve();
          ?????}
          ?????return?fileStrategy;
          ?}

          其實這就是工廠模式,定義一個創(chuàng)建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創(chuàng)建過程延遲到子類進行。

          策略模式的例子,沒有使用上一段代碼,而是借助spring的特性,搞了一個工廠模式,哈哈,小伙伴們可以回去那個例子細(xì)品一下,我把代碼再搬下來,小伙伴們再品一下吧:

          /**
          ?*??@author 公眾號:撿田螺的小男孩
          ?*/
          @Component
          public?class?StrategyUseService?implements?ApplicationContextAware{

          ????private?Map?iFileStrategyMap?=?new?ConcurrentHashMap<>();

          ????//把所有的文件類型解析的對象,放到map,需要使用時,信手拈來即可。這就是工廠模式的一種體現(xiàn)啦
          ????@Override
          ????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
          ????????Map?tmepMap?=?applicationContext.getBeansOfType(IFileStrategy.class);
          ????????tmepMap.values().forEach(strategyService?->?iFileStrategyMap.put(strategyService.gainFileType(),?strategyService));
          ????}
          }

          5.2 使用工廠模式

          定義工廠模式也是比較簡單的:

          • 一個工廠接口,提供一個創(chuàng)建不同對象的方法。
          • 其子類實現(xiàn)工廠接口,構(gòu)造不同對象
          • 使用工廠模式

          5.3.1 一個工廠接口

          interface?IFileResolveFactory{
          ???void?resolve();
          }

          5.3.2 不同子類實現(xiàn)工廠接口

          class?AFileResolve?implements?IFileResolveFactory{
          ???void?resolve(){
          ??????System.out.println("文件A類型解析");
          ???}
          }

          class?BFileResolve?implements?IFileResolveFactory{
          ???void?resolve(){
          ??????System.out.println("文件B類型解析");
          ???}
          }

          class?DefaultFileResolve?implements?IFileResolveFactory{
          ???void?resolve(){
          ??????System.out.println("默認(rèn)文件類型解析");
          ???}
          }

          5.3.3 使用工廠模式

          //構(gòu)造不同的工廠對象
          IFileResolveFactory?fileResolveFactory;
          if(fileType=“A”){
          ????fileResolveFactory?=?new?AFileResolve();
          }else?if(fileType=“B”){
          ????fileResolveFactory?=?new?BFileResolve();
          ?}else{
          ????fileResolveFactory?=?new?DefaultFileResolve();
          }

          fileResolveFactory.resolve();

          一般情況下,對于工廠模式,你不會看到以上的代碼。工廠模式會跟配合其他設(shè)計模式如策略模式一起出現(xiàn)的。

          6. 單例模式

          6.1 業(yè)務(wù)場景

          單例模式,保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。I/O與數(shù)據(jù)庫的連接,一般就用單例模式實現(xiàn)de的。Windows里面的Task Manager(任務(wù)管理器)也是很典型的單例模式。

          來看一個單例模式的例子

          /**
          ?*??公眾號:撿田螺的小男孩
          ?*/
          public?class?LanHanSingleton?{

          ????private?static?LanHanSingleton?instance;

          ????private?LanHanSingleton(){

          ????}

          ????public?static?LanHanSingleton?getInstance(){
          ????????if?(instance?==?null)?{
          ????????????instance?=?new?LanHanSingleton();
          ????????}
          ????????return?instance;
          ????}

          }
          ?

          以上的例子,就是懶漢式的單例實現(xiàn)。實例在需要用到的時候,才去創(chuàng)建,就比較懶。如果有則返回,沒有則新建,需要加下 synchronized關(guān)鍵字,要不然可能存在線性安全問題。

          6.2 單例模式的經(jīng)典寫法

          其實單例模式還有有好幾種實現(xiàn)方式,如餓漢模式,雙重校驗鎖,靜態(tài)內(nèi)部類,枚舉等實現(xiàn)方式。

          6.2.1 餓漢模式

          public?class?EHanSingleton?{

          ???private?static?EHanSingleton?instance?=?new?EHanSingleton();
          ???
          ???private?EHanSingleton(){??????
          ???}

          ???public?static?EHanSingleton?getInstance()?{
          ???????return?instance;
          ???}
          ???
          }

          餓漢模式,它比較饑餓、比較勤奮,實例在初始化的時候就已經(jīng)建好了,不管你后面有沒有用到,都先新建好實例再說。這個就沒有線程安全的問題,但是呢,浪費內(nèi)存空間呀。

          6.2.2 雙重校驗鎖

          public?class?DoubleCheckSingleton?{

          ???private?volatile static?DoubleCheckSingleton?instance;

          ???private?DoubleCheckSingleton()?{?}
          ???
          ???public?static?DoubleCheckSingleton?getInstance(){
          ???????if?(instance?==?null)?{
          ???????????synchronized?(DoubleCheckSingleton.class)?{
          ???????????????if?(instance?==?null)?{
          ???????????????????instance?=?new?DoubleCheckSingleton();
          ???????????????}
          ???????????}
          ???????}
          ???????return?instance;
          ???}
          }

          雙重校驗鎖實現(xiàn)的單例模式,綜合了懶漢式和餓漢式兩者的優(yōu)缺點。以上代碼例子中,在synchronized關(guān)鍵字內(nèi)外都加了一層 ?if條件判斷,這樣既保證了線程安全,又比直接上鎖提高了執(zhí)行效率,還節(jié)省了內(nèi)存空間。

          6.2.3 靜態(tài)內(nèi)部類

          public?class?InnerClassSingleton?{

          ???private?static?class?InnerClassSingletonHolder{
          ???????private?static?final?InnerClassSingleton?INSTANCE?=?new?InnerClassSingleton();
          ???}

          ???private?InnerClassSingleton(){}
          ???
          ???public?static?final?InnerClassSingleton?getInstance(){
          ???????return?InnerClassSingletonHolder.INSTANCE;
          ???}
          }

          靜態(tài)內(nèi)部類的實現(xiàn)方式,效果有點類似雙重校驗鎖。但這種方式只適用于靜態(tài)域場景,雙重校驗鎖方式可在實例域需要延遲初始化時使用。

          6.2.4 枚舉

          public?enum?SingletonEnum?{

          ????INSTANCE;
          ????public?SingletonEnum?getInstance(){
          ????????return?INSTANCE;
          ????}
          }

          枚舉實現(xiàn)的單例,代碼簡潔清晰。并且它還自動支持序列化機制,絕對防止多次實例化。

          求點贊、在看、分享三連

          瀏覽 43
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  看女人18毛片 | 色鸡鸡综合网 | 黄色视频链接在线观看 | 国产乱码一区二区三区的解决方法 | 五月天成人网址 |