<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>

          講解Spring中如何使用策略模式,工廠方法模式以及Builder模式。

          共 6888字,需瀏覽 14分鐘

           ·

          2020-09-03 10:57

          來源:網(wǎng)絡(luò)

          • 1. 策略模式
          • 2. 工廠方法模式
          • 3. Builder模式
          • 4. 小結(jié)

          關(guān)于設(shè)計(jì)模式,如果使用得當(dāng),將會使我們的代碼更加簡潔,并且更具擴(kuò)展性。本文主要講解Spring中如何使用策略模式,工廠方法模式以及Builder模式。

          1. 策略模式

          關(guān)于策略模式的使用方式,在Spring中其實(shí)比較簡單,從本質(zhì)上講,策略模式就是一個(gè)接口下有多個(gè)實(shí)現(xiàn)類,而每種實(shí)現(xiàn)類會處理某一種情況。

          我們以發(fā)獎(jiǎng)勵(lì)為例進(jìn)行講解,比如我們在抽獎(jiǎng)系統(tǒng)中,有多種獎(jiǎng)勵(lì)方式可供選擇,比如積分,虛擬幣和現(xiàn)金等。在存儲時(shí),我們必然會使用一個(gè)類似于type的字段用于表征這幾種發(fā)放獎(jiǎng)勵(lì)的,那么這里我們就可以使用多態(tài)的方式進(jìn)行獎(jiǎng)勵(lì)的發(fā)放。比如我們抽象出一個(gè)PrizeSender的接口,其聲明如下:

          public?interface?PrizeSender?{

          ??/**
          ???*?用于判斷當(dāng)前實(shí)例是否支持當(dāng)前獎(jiǎng)勵(lì)的發(fā)放
          ???*/

          ??boolean?support(SendPrizeRequest?request);

          ??/**
          ???*?發(fā)放獎(jiǎng)勵(lì)
          ???*/

          ??void?sendPrize(SendPrizeRequest?request);

          }

          該接口中主要有兩個(gè)方法:support()和sendPrize(),其中support()方法主要用于判斷各個(gè)子類是否支持當(dāng)前類型數(shù)據(jù)的處理,而sendPrize()則主要是用于進(jìn)行具體的業(yè)務(wù)處理的,比如這里獎(jiǎng)勵(lì)的發(fā)放。下面就是我們?nèi)N不同類型的獎(jiǎng)勵(lì)發(fā)放的具體代碼:

          //?積分發(fā)放
          @Component
          public?class?PointSender?implements?PrizeSender?{

          ??@Override
          ??public?boolean?support(SendPrizeRequest?request)?{
          ????return?request.getPrizeType()?==?PrizeTypeEnum.POINT;
          ??}

          ??@Override
          ??public?void?sendPrize(SendPrizeRequest?request)?{
          ????System.out.println("發(fā)放積分");
          ??}
          }
          //?虛擬幣發(fā)放
          @Component
          public?class?VirtualCurrencySender?implements?PrizeSender?{

          ??@Override
          ??public?boolean?support(SendPrizeRequest?request)?{
          ????return?PrizeTypeEnum.VIRTUAL_CURRENCY?==?request.getPrizeType();
          ??}

          ??@Override
          ??public?void?sendPrize(SendPrizeRequest?request)?{
          ????System.out.println("發(fā)放虛擬幣");
          ??}
          }
          //?現(xiàn)金發(fā)放
          @Component
          public?class?CashSender?implements?PrizeSender?{

          ??@Override
          ??public?boolean?support(SendPrizeRequest?request)?{
          ????return?PrizeTypeEnum.CASH?==?request.getPrizeType();
          ??}

          ??@Override
          ??public?void?sendPrize(SendPrizeRequest?request)?{
          ????System.out.println("發(fā)放現(xiàn)金");
          ??}
          }

          這里可以看到,在每種子類型中,我們只需要在support()方法中通過request的某個(gè)參數(shù)來控制當(dāng)前request是否是當(dāng)前實(shí)例能夠處理的類型,如果是,則外層的控制邏輯就會將request交給當(dāng)前實(shí)例進(jìn)行處理。關(guān)于這個(gè)類的設(shè)計(jì),有幾個(gè)點(diǎn)需要注意:

          • 使用@Component注解對當(dāng)前類進(jìn)行標(biāo)注,將其聲明為Spring容器所管理的一個(gè)bean;
          • 聲明一個(gè)返回boolean值的類似于support()的方法,通過這個(gè)方法來控制當(dāng)前實(shí)例是否為處理目標(biāo)request的實(shí)例;
          • 聲明一個(gè)類似于sendPrize()的方法用于處理業(yè)務(wù)邏輯,當(dāng)然根據(jù)各個(gè)業(yè)務(wù)的不同聲明的方法名肯定是不同的,這里只是一個(gè)對統(tǒng)一的業(yè)務(wù)處理的抽象;
          • 無論是support()方法還是sendPrize()方法,都需要傳一個(gè)對象進(jìn)行,而不是簡簡單單的基本類型的變量,這樣做的好處是后續(xù)如果要在Request中新增字段,那么就不需要修改接口的定義和已經(jīng)實(shí)現(xiàn)的各個(gè)子類的邏輯;

          2. 工廠方法模式

          上面我們講解了如何使用Spring來聲明一個(gè)策略模式,那么如何為不同的業(yè)務(wù)邏輯來注入不同的bean呢,或者說外層的控制邏輯是什么樣的,這里我們就可以使用工廠方法模式了。

          所謂的工廠方法模式,就是定義一個(gè)工廠方法,通過傳入的參數(shù),返回某個(gè)實(shí)例,然后通過該實(shí)例來處理后續(xù)的業(yè)務(wù)邏輯。一般的,工廠方法的返回值類型是一個(gè)接口類型,而選擇具體子類實(shí)例的邏輯則封裝到了工廠方法中了。通過這種方式,來將外層調(diào)用邏輯與具體的子類的獲取邏輯進(jìn)行分離。如下圖展示了工廠方法模式的一個(gè)示意圖:

          可以看到,工廠方法將具體實(shí)例的選擇進(jìn)行了封裝,而客戶端,也就是我們的調(diào)用方只需要調(diào)用工廠的具體方法獲取到具體的事例即可,而不需要管具體的實(shí)例實(shí)現(xiàn)是什么。

          上面我們講解了Spring中是如何使用策略模式聲明處理邏輯的,而沒有講如何選擇具體的策略,這里我們就可以使用工廠方法模式。

          如下是我們聲明的一個(gè)PrizeSenderFactory:

          @Component
          public?class?PrizeSenderFactory?{

          ??@Autowired
          ??private?List?prizeSenders;

          ??public?PrizeSender?getPrizeSender(SendPrizeRequest?request)?{
          ????for?(PrizeSender?prizeSender?:?prizeSenders)?{
          ??????if?(prizeSender.support(request))?{
          ????????return?prizeSender;
          ??????}
          ????}

          ????throw?new?UnsupportedOperationException("unsupported?request:?"?+?request);
          ??}
          }

          這里我們聲明一個(gè)了一個(gè)工廠方法getPrizeSender(),其入?yún)⒕褪荢endPrizeRequest,而返回值是某個(gè)實(shí)現(xiàn)了PrizeSender接口的實(shí)例,可以看到,通過這種方式,我們將具體的選擇方式下移到了具體的子類中的,因?yàn)楫?dāng)前實(shí)現(xiàn)了PrizeSender的bean是否支持當(dāng)前request的處理,是由具體的子類實(shí)現(xiàn)的。

          在該工廠方法中,我們也沒有任何與具體子類相關(guān)的邏輯,也就是說,該類實(shí)際上是可以動(dòng)態(tài)檢測新加入的子類實(shí)例的。這主要是通過Spring的自動(dòng)注入來實(shí)現(xiàn)的,主要是因?yàn)槲覀冞@里注入的是一個(gè)List,也就是說,如果有新的PrizeSender的子類實(shí)例,只要其是Spring所管理的,那么都會被注入到這里來。下面就是我們編寫的一段用于測試的代碼來模擬調(diào)用方的調(diào)用:

          @Service
          public?class?ApplicationService?{

          ??@Autowired
          ??private?PrizeSenderFactory?prizeSenderFactory;

          ??public?void?mockedClient()?{
          ????SendPrizeRequest?request?=?new?SendPrizeRequest();
          ????request.setPrizeType(PrizeTypeEnum.POINT);??//?這里的request一般是根據(jù)數(shù)據(jù)庫或外部調(diào)用來生成的
          ????PrizeSender?prizeSender?=?prizeSenderFactory.getPrizeSender(request);
          ????prizeSender.sendPrize(request);
          ??}
          }

          在客戶端代碼中,首先通過PrizeSenderFactory獲取一個(gè)PrizeSender實(shí)例,然后通過其sendPrize()方法發(fā)放具體的獎(jiǎng)勵(lì),通過這種方式,將具體的獎(jiǎng)勵(lì)發(fā)放邏輯與客戶端調(diào)用進(jìn)行了解耦。而且根據(jù)前面的講解,我們也知道,如果新增了一種獎(jiǎng)勵(lì)方式,我們只需要聲明一個(gè)新的實(shí)現(xiàn)了PrizeSender的bean即可,而不需要對現(xiàn)有代碼進(jìn)行任何修改。

          3. Builder模式

          關(guān)于Builder模式,我想使用過lombok的同學(xué)肯定會說builder模式非常的簡單,只需要在某個(gè)bean上使用@Builder注解進(jìn)行聲明即可,lombok可以自動(dòng)幫我們將其聲明為一個(gè)Builder的bean。關(guān)于這種使用方式,本人不置可否,不過就我的理解,這里主要有兩個(gè)點(diǎn)我們需要理解:

          1、Builder模式就其名稱而言,是一個(gè)構(gòu)建者,我更傾向于將其理解為通過一定的參數(shù),通過一定的業(yè)務(wù)邏輯來最終生成某個(gè)對象。如果僅僅只是使用lombok的這種方式,其本質(zhì)上也還是創(chuàng)建了一個(gè)簡單的bean,這個(gè)與通過getter和setter方式構(gòu)建一個(gè)bean是沒有什么大的區(qū)別的;

          2、在Spring框架中,使用設(shè)計(jì)模式最大的問題在于如果在各個(gè)模式bean中能夠注入Spring的bean,如果能夠注入,那么將大大的擴(kuò)展其使用方式。因?yàn)槲覀兙涂梢哉娴膶?shí)現(xiàn)通過傳入的簡單的幾個(gè)參數(shù),然后結(jié)合Spring注入的bean進(jìn)行一定的處理后,以構(gòu)造出我們所需要的某個(gè)bean。顯然,這是lombok所無法實(shí)現(xiàn)的;

          關(guān)于Builder模式,我們可以以前面獎(jiǎng)勵(lì)發(fā)放的SendPrizeRequest的構(gòu)造為例進(jìn)行講解。在構(gòu)造request對象的時(shí)候,必然是通過前臺傳如的某些參數(shù)來經(jīng)過一定的處理,最后生成一個(gè)request對象。那么我們就可以使用Builder模式來構(gòu)建一個(gè)SendPrizeRequest。

          這里假設(shè)根據(jù)前臺調(diào)用,我們能夠獲取到prizeId和userId,那么我們就可以創(chuàng)建一個(gè)如下的SendPrizeRequest:

          public?class?SendPrizeRequest?{

          ??private?final?PrizeTypeEnum?prizeType;
          ??private?final?int?amount;
          ??private?final?String?userId;

          ??public?SendPrizeRequest(PrizeTypeEnum?prizeType,?int?amount,?String?userId)?{
          ????this.prizeType?=?prizeType;
          ????this.amount?=?amount;
          ????this.userId?=?userId;
          ??}

          ??@Component
          ??@Scope("prototype")
          ??public?static?class?Builder?{

          ????@Autowired
          ????PrizeService?prizeService;

          ????private?int?prizeId;
          ????private?String?userId;

          ????public?Builder?prizeId(int?prizeId)?{
          ??????this.prizeId?=?prizeId;
          ??????return?this;
          ????}

          ????public?Builder?userId(String?userId)?{
          ??????this.userId?=?userId;
          ??????return?this;
          ????}

          ????public?SendPrizeRequest?build()?{
          ??????Prize?prize?=?prizeService.findById(prizeId);
          ??????return?new?SendPrizeRequest(prize.getPrizeType(),?prize.getAmount(),?userId);
          ????}
          ??}

          ??public?PrizeTypeEnum?getPrizeType()?{
          ????return?prizeType;
          ??}

          ??public?int?getAmount()?{
          ????return?amount;
          ??}

          ??public?String?getUserId()?{
          ????return?userId;
          ??}
          }

          這里就是使用Spring維護(hù)一個(gè)Builder模式的示例,具體的 維護(hù)方式就是在Builder類上使用@Component和@Scope注解來標(biāo)注該Builder類,這樣我們就可以在Builder類中注入我們所需要的實(shí)例來進(jìn)行一定的業(yè)務(wù)處理了。關(guān)于該模式,這里有幾點(diǎn)需要說明:

          • 在Builder類上必須使用@Scope注解來標(biāo)注該實(shí)例為prototype類型,因?yàn)楹苊黠@,我們這里的Builder實(shí)例是有狀態(tài)的,無法被多線程共享;
          • 在Builder.build()方法中,我們可以通過傳入的參數(shù)和注入的bean來進(jìn)行一定的業(yè)務(wù)處理,從而得到構(gòu)建一個(gè)SendPrizeRequest所需要的參數(shù);
          • Builder類必須使用static修飾,因?yàn)樵贘ava中,如果內(nèi)部類不用static修飾,那么該類的實(shí)例必須依賴于外部類的一個(gè)實(shí)例,而我們這里本質(zhì)上是希望通過內(nèi)部類實(shí)例來構(gòu)建外部類實(shí)例,也就是說內(nèi)部類實(shí)例存在的時(shí)候,外部類實(shí)例是還不存在的,因而這里必須使用static修飾;
          • 根據(jù)標(biāo)準(zhǔn)的Builder模式的使用方式,外部類的各個(gè)參數(shù)都必須使用final修飾,然后只需要為其聲明getter方法即可。

          上面我們展示了如何使用Spring的方式來聲明一個(gè)Builder模式的類,那么我們該如何進(jìn)行使用呢,如下是我們的一個(gè)使用示例:

          @Service
          public?class?ApplicationService?{

          ??@Autowired
          ??private?PrizeSenderFactory?prizeSenderFactory;

          ??@Autowired
          ??private?ApplicationContext?context;

          ??public?void?mockedClient()?{
          ????SendPrizeRequest?request?=?newPrizeSendRequestBuilder()
          ????????.prizeId(1)
          ????????.userId("u4352234")
          ????????.build();

          ????PrizeSender?prizeSender?=?prizeSenderFactory.getPrizeSender(request);
          ????prizeSender.sendPrize(request);
          ??}

          ??public?Builder?newPrizeSendRequestBuilder()?{
          ????return?context.getBean(Builder.class);
          ??}
          }

          上述代碼中,我們主要要看一下newPrizeSendRequestBuilder()方法,在Spring中,如果一個(gè)類是多例類型,也即使用@Scope("prototype")進(jìn)行了標(biāo)注,那么每次獲取該bean的時(shí)候就必須使用ApplicationContext.getBean()方法獲取一個(gè)新的實(shí)例,至于具體的原因,讀者可查閱相關(guān)文檔。

          我們這里就是通過一個(gè)單獨(dú)的方法來創(chuàng)建一個(gè)Builder對象,然后通過流式來為其設(shè)置prizeId和userId等參數(shù),最后通過build()方法構(gòu)建得到了一個(gè)SendPrizeRequest實(shí)例,通過該實(shí)例來進(jìn)行后續(xù)的獎(jiǎng)勵(lì)發(fā)放。

          4. 小結(jié)

          本文主要通過一個(gè)獎(jiǎng)勵(lì)發(fā)放的示例來對Spring中如何使用工廠方法模式,策略模式和Builder模式的方式進(jìn)行講解,并且著重強(qiáng)調(diào)了實(shí)現(xiàn)各個(gè)模式時(shí)我們所需要注意的點(diǎn)。

          - 推薦閱讀 -


          架構(gòu)師離職后,成為自由開發(fā)者的第 100 天


          下方二維碼關(guān)注我

          互聯(lián)網(wǎng)草根,堅(jiān)持分享技術(shù)創(chuàng)業(yè)產(chǎn)品心得和總結(jié)~



          點(diǎn)擊“閱讀原文”,領(lǐng)取 2020 年最新免費(fèi)技術(shù)資料大全

          ↓↓↓?
          瀏覽 77
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  五月丁香婷婷色综合 | 樱桃视频黄色 | 中国最新毛片 | 中文字幕一区二区二三区四区 | 人人爽,人人妻,人人操 |