乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)工廠方法模式「多種類型商品不同接口,統(tǒng)一發(fā)獎(jiǎng)服務(wù)搭建場(chǎng)景」

       小傅哥 2021-12-13


      作者:小傅哥
      博客:https://

      沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!😄

      一、前言

      好看的代碼千篇一律,惡心的程序升職加薪。

      該說(shuō)不說(shuō)幾乎是程序員就都知道或者了解設(shè)計(jì)模式,但大部分小伙伴寫代碼總是習(xí)慣于一把梭。無(wú)論多少業(yè)務(wù)邏輯就一個(gè)類幾千行,這樣的開發(fā)也可以歸納為三步;定義屬性、創(chuàng)建方法、調(diào)用展示,Done!只不過(guò)開發(fā)一時(shí)爽,重構(gòu)火葬場(chǎng)。

      好的代碼不只為了完成現(xiàn)有功能,也會(huì)考慮后續(xù)擴(kuò)展。在結(jié)構(gòu)設(shè)計(jì)上松耦合易讀易擴(kuò)展,在領(lǐng)域?qū)崿F(xiàn)上高內(nèi)聚不對(duì)外暴漏實(shí)現(xiàn)細(xì)節(jié)不被外部干擾。而這就有點(diǎn)像家里三居(MVC)室、四居(DDD)室的裝修,你不會(huì)允許幾十萬(wàn)的房子把走線水管裸漏在外面,也不會(huì)允許把馬桶放到廚房,爐灶安裝到衛(wèi)生間。

      誰(shuí)發(fā)明了設(shè)計(jì)模式? 設(shè)計(jì)模式的概念最早是由 克里斯托佛·亞歷山大 在其著作 《建筑模式語(yǔ)言》 中首次提出的。 本書介紹了城市設(shè)計(jì)的 “語(yǔ)言”,提供了253個(gè)描述城鎮(zhèn)、鄰里、住宅、花園、房間及西部構(gòu)造的模式, 而此類 “語(yǔ)言” 的基本單元就是模式。后來(lái),埃里?!べが?/code>、 約翰·弗利賽德斯、 拉爾夫·約翰遜理查德·赫爾姆 這四位作者接受了模式的概念。 1994 年, 他們出版了 《設(shè)計(jì)模式: 可復(fù)用面向?qū)ο筌浖幕A(chǔ)》 一書, 將設(shè)計(jì)模式的概念應(yīng)用到程序開發(fā)領(lǐng)域中。

      其實(shí)有一部分人并沒有仔細(xì)閱讀過(guò)設(shè)計(jì)模式的相關(guān)書籍和資料,但依舊可以編寫出優(yōu)秀的代碼。這主要是由于在經(jīng)過(guò)眾多項(xiàng)目的錘煉和對(duì)程序設(shè)計(jì)的不斷追求,從而在多年編程歷程上提煉出來(lái)的心得體會(huì)。而這份經(jīng)驗(yàn)最終會(huì)與設(shè)計(jì)模式提到的內(nèi)容幾乎一致,同樣會(huì)要求高內(nèi)聚、低耦合、可擴(kuò)展、可復(fù)用。你可能也遇到類似的經(jīng)歷,在學(xué)習(xí)一些框架的源碼時(shí),發(fā)現(xiàn)它里的某些設(shè)計(jì)和你在做開發(fā)時(shí)一樣。

      我怎么學(xué)不會(huì)設(shè)計(jì)模式? 錢也花了,書也買了。代碼還是一坨一坨的!設(shè)計(jì)模式是由多年的經(jīng)驗(yàn)提煉出來(lái)開發(fā)指導(dǎo)思想。就像我告訴你自行車怎么騎、汽車怎么開,但只要你沒跑過(guò)幾千公里,你能記住的只是理論,想上道依舊很慌!

      所以,本設(shè)計(jì)模式專題系列開始,會(huì)帶著你使用設(shè)計(jì)模式的思想去優(yōu)化代碼。從而學(xué)習(xí)設(shè)計(jì)模式的心得并融入給自己。當(dāng)然這里還需要多加練習(xí),一定是人車合一,才能站在設(shè)計(jì)模式的基礎(chǔ)上構(gòu)建出更加合理的代碼。

      二、開發(fā)環(huán)境

      1. JDK 1.8

      2. Idea + Maven

      3. 涉及工程三個(gè),可以通過(guò)關(guān)注公眾號(hào)bugstack蟲洞棧,回復(fù)源碼下載獲取。你會(huì)獲得一個(gè)連接打開后的列表中編號(hào)18itstack-demo-design

        工程描述
        itstack-demo-design-1-00場(chǎng)景模擬工程,用于提供三組不同獎(jiǎng)品的發(fā)放接口
        itstack-demo-design-1-01使用一坨代碼實(shí)現(xiàn)業(yè)務(wù)需求,也是對(duì)ifelse的使用
        itstack-demo-design-1-02通過(guò)設(shè)計(jì)模式優(yōu)化改造代碼,產(chǎn)生對(duì)比性從而學(xué)習(xí)
        • 1-00,1 代表著第一個(gè)設(shè)計(jì)模式,工廠方法模式
        • 1-00,00 代表模擬的場(chǎng)景
        • 1-01,01 代表第一種實(shí)現(xiàn)方案,后續(xù) 02 03 以此類推

      二、工廠方法模式介紹

      工廠方法模式,圖片來(lái)自 refactoringguru.cn

      • 工廠方法模式,圖片來(lái)自 refactoringguru.cn

      工廠模式又稱工廠方法模式,是一種創(chuàng)建型設(shè)計(jì)模式,其在父類中提供一個(gè)創(chuàng)建對(duì)象的方法, 允許子類決定實(shí)例化對(duì)象的類型。

      這種設(shè)計(jì)模式也是 Java 開發(fā)中最常見的一種模式,它的主要意圖是定義一個(gè)創(chuàng)建對(duì)象的接口,讓其子類自己決定實(shí)例化哪一個(gè)工廠類,工廠模式使其創(chuàng)建過(guò)程延遲到子類進(jìn)行。

      簡(jiǎn)單說(shuō)就是為了提供代碼結(jié)構(gòu)的擴(kuò)展性,屏蔽每一個(gè)功能類中的具體實(shí)現(xiàn)邏輯。讓外部可以更加簡(jiǎn)單的只是知道調(diào)用即可,同時(shí),這也是去掉眾多ifeslse的方式。當(dāng)然這可能也有一些缺點(diǎn),比如需要實(shí)現(xiàn)的類非常多,如何去維護(hù),怎樣減低開發(fā)成本。但這些問(wèn)題都可以在后續(xù)的設(shè)計(jì)模式結(jié)合使用中,逐步降低。

      三、模擬發(fā)獎(jiǎng)多種商品

      模擬發(fā)獎(jiǎng)多種商品,bugstack蟲洞棧

      為了可以讓整個(gè)學(xué)習(xí)的案例更加貼近實(shí)際開發(fā),這里模擬互聯(lián)網(wǎng)中在營(yíng)銷場(chǎng)景下的業(yè)務(wù)。由于營(yíng)銷場(chǎng)景的復(fù)雜、多變、臨時(shí)的特性,它所需要的設(shè)計(jì)需要更加深入,否則會(huì)經(jīng)常面臨各種緊急CRUD操作,從而讓代碼結(jié)構(gòu)混亂不堪,難以維護(hù)。

      在營(yíng)銷場(chǎng)景中經(jīng)常會(huì)有某個(gè)用戶做了一些操作;打卡、分享、留言、邀請(qǐng)注冊(cè)等等,進(jìn)行返利積分,最后通過(guò)積分在兌換商品,從而促活和拉新。

      那么在這里我們模擬積分兌換中的發(fā)放多種類型商品,假如現(xiàn)在我們有如下三種類型的商品接口;

      序號(hào)類型接口
      1優(yōu)惠券CouponResult sendCoupon(String uId, String couponNumber, String uuid)
      2實(shí)物商品Boolean deliverGoods(DeliverReq req)
      3第三方愛奇藝兌換卡void grantToken(String bindMobileNumber, String cardId)

      從以上接口來(lái)看有如下信息:

      • 三個(gè)接口返回類型不同,有對(duì)象類型、布爾類型、還有一個(gè)空類型。
      • 入?yún)⒉煌?#xff0c;發(fā)放優(yōu)惠券需要仿重、兌換卡需要卡ID、實(shí)物商品需要發(fā)貨位置(對(duì)象中含有)。
      • 另外可能會(huì)隨著后續(xù)的業(yè)務(wù)的發(fā)展,會(huì)新增其他種商品類型。因?yàn)槟闼械拈_發(fā)需求都是隨著業(yè)務(wù)對(duì)市場(chǎng)的拓展而帶來(lái)的。

      四、用一坨坨代碼實(shí)現(xiàn)

      如果不考慮任何擴(kuò)展性,只為了盡快滿足需求,那么對(duì)這么幾種獎(jiǎng)勵(lì)發(fā)放只需使用ifelse語(yǔ)句判斷,調(diào)用不同的接口即可滿足需求??赡苓@也是一些剛?cè)腴T編程的小伙伴,常用的方式。接下來(lái)我們就先按照這樣的方式來(lái)實(shí)現(xiàn)業(yè)務(wù)的需求。

      1. 工程結(jié)構(gòu)

      itstack-demo-design-1-01
      └── src
          ├── main
          │   └── java
          │       └── org.itstack.demo.design
          │           ├── AwardReq.java
          │           ├── AwardRes.java
          │           └── PrizeController.java 
          └── test
               └── java
                   └── org.itstack.demo.design.test
                       └── ApiTest.java
      
      • 工程結(jié)構(gòu)上非常簡(jiǎn)單,一個(gè)入?yún)?duì)象 AwardReq 、一個(gè)出參對(duì)象 AwardRes,以及一個(gè)接口類 PrizeController

      2. ifelse實(shí)現(xiàn)需求

      public class PrizeController {
      
          private Logger logger = LoggerFactory.getLogger(PrizeController.class);
      
          public AwardRes awardToUser(AwardReq req) {
              String reqJson = JSON.toJSONString(req);
              AwardRes awardRes = null;
              try {
                  logger.info("獎(jiǎng)品發(fā)放開始{}。req:{}", req.getuId(), reqJson);
                  // 按照不同類型方法商品[1優(yōu)惠券、2實(shí)物商品、3第三方兌換卡(愛奇藝)]
                  if (req.getAwardType() == 1) {
                      CouponService couponService = new CouponService();
                      CouponResult couponResult = couponService.sendCoupon(req.getuId(), req.getAwardNumber(), req.getBizId());
                      if ("0000".equals(couponResult.getCode())) {
                          awardRes = new AwardRes("0000", "發(fā)放成功");
                      } else {
                          awardRes = new AwardRes("0001", couponResult.getInfo());
                      }
                  } else if (req.getAwardType() == 2) {
                      GoodsService goodsService = new GoodsService();
                      DeliverReq deliverReq = new DeliverReq();
                      deliverReq.setUserName(queryUserName(req.getuId()));
                      deliverReq.setUserPhone(queryUserPhoneNumber(req.getuId()));
                      deliverReq.setSku(req.getAwardNumber());
                      deliverReq.setOrderId(req.getBizId());
                      deliverReq.setConsigneeUserName(req.getExtMap().get("consigneeUserName"));
                      deliverReq.setConsigneeUserPhone(req.getExtMap().get("consigneeUserPhone"));
                      deliverReq.setConsigneeUserAddress(req.getExtMap().get("consigneeUserAddress"));
                      Boolean isSuccess = goodsService.deliverGoods(deliverReq);
                      if (isSuccess) {
                          awardRes = new AwardRes("0000", "發(fā)放成功");
                      } else {
                          awardRes = new AwardRes("0001", "發(fā)放失敗");
                      }
                  } else if (req.getAwardType() == 3) {
                      String bindMobileNumber = queryUserPhoneNumber(req.getuId());
                      IQiYiCardService iQiYiCardService = new IQiYiCardService();
                      iQiYiCardService.grantToken(bindMobileNumber, req.getAwardNumber());
                      awardRes = new AwardRes("0000", "發(fā)放成功");
                  }
                  logger.info("獎(jiǎng)品發(fā)放完成{}。", req.getuId());
              } catch (Exception e) {
                  logger.error("獎(jiǎng)品發(fā)放失敗{}。req:{}", req.getuId(), reqJson, e);
                  awardRes = new AwardRes("0001", e.getMessage());
              }
      
              return awardRes;
          }
      
          private String queryUserName(String uId) {
              return "花花";
          }
      
          private String queryUserPhoneNumber(String uId) {
              return "15200101232";
          }
      
      }
      
      • 如上就是使用 ifelse 非常直接的實(shí)現(xiàn)出來(lái)業(yè)務(wù)需求的一坨代碼,如果僅從業(yè)務(wù)角度看,研發(fā)如期甚至提前實(shí)現(xiàn)了功能。
      • 那這樣的代碼目前來(lái)看并不會(huì)有什么問(wèn)題,但如果在經(jīng)過(guò)幾次的迭代和拓展,接手這段代碼的研發(fā)將十分痛苦。重構(gòu)成本高需要理清之前每一個(gè)接口的使用,測(cè)試回歸驗(yàn)證時(shí)間長(zhǎng),需要全部驗(yàn)證一次。這也就是很多人并不愿意接手別人的代碼,如果接手了又被壓榨開發(fā)時(shí)間。那么可想而知這樣的 ifelse 還會(huì)繼續(xù)增加。

      3. 測(cè)試驗(yàn)證

      寫一個(gè)單元測(cè)試來(lái)驗(yàn)證上面編寫的接口方式,養(yǎng)成單元測(cè)試的好習(xí)慣會(huì)為你增強(qiáng)代碼質(zhì)量。

      編寫測(cè)試類:

      @Test
      public void test_awardToUser() {
          PrizeController prizeController = new PrizeController();
          System.out.println("\r\n模擬發(fā)放優(yōu)惠券測(cè)試\r\n");
          // 模擬發(fā)放優(yōu)惠券測(cè)試
          AwardReq req01 = new AwardReq();
          req01.setuId("10001");
          req01.setAwardType(1);
          req01.setAwardNumber("EGM1023938910232121323432");
          req01.setBizId("791098764902132");
          AwardRes awardRes01 = prizeController.awardToUser(req01);
          logger.info("請(qǐng)求參數(shù):{}", JSON.toJSON(req01));
          logger.info("測(cè)試結(jié)果:{}", JSON.toJSON(awardRes01));
          System.out.println("\r\n模擬方法實(shí)物商品\r\n");
          // 模擬方法實(shí)物商品
          AwardReq req02 = new AwardReq();
          req02.setuId("10001");
          req02.setAwardType(2);
          req02.setAwardNumber("9820198721311");
          req02.setBizId("1023000020112221113");
          Map<String,String> extMap = new HashMap<String,String>();
          extMap.put("consigneeUserName", "謝飛機(jī)");
          extMap.put("consigneeUserPhone", "15200292123");
          extMap.put("consigneeUserAddress", "吉林省.長(zhǎng)春市.雙陽(yáng)區(qū).XX街道.檀溪苑小區(qū).#18-2109");
          req02.setExtMap(extMap);
      
          commodityService_2.sendCommodity("10001","9820198721311","1023000020112221113", extMap);
      
          AwardRes awardRes02 = prizeController.awardToUser(req02);
          logger.info("請(qǐng)求參數(shù):{}", JSON.toJSON(req02));
          logger.info("測(cè)試結(jié)果:{}", JSON.toJSON(awardRes02));
          System.out.println("\r\n第三方兌換卡(愛奇藝)\r\n");
          AwardReq req03 = new AwardReq();
          req03.setuId("10001");
          req03.setAwardType(3);
          req03.setAwardNumber("AQY1xjkUodl8LO975GdfrYUio");
          AwardRes awardRes03 = prizeController.awardToUser(req03);
          logger.info("請(qǐng)求參數(shù):{}", JSON.toJSON(req03));
          logger.info("測(cè)試結(jié)果:{}", JSON.toJSON(awardRes03));
      }
      

      結(jié)果:

      模擬發(fā)放優(yōu)惠券測(cè)試
      
      22:17:55.668 [main] INFO  o.i.demo.design.PrizeController - 獎(jiǎng)品發(fā)放開始10001。req:{"awardNumber":"EGM1023938910232121323432","awardType":1,"bizId":"791098764902132","uId":"10001"}
      模擬發(fā)放優(yōu)惠券一張:10001,EGM1023938910232121323432,791098764902132
      22:17:55.671 [main] INFO  o.i.demo.design.PrizeController - 獎(jiǎng)品發(fā)放完成10001。
      22:17:55.673 [main] INFO  org.itstack.demo.test.ApiTest - 請(qǐng)求參數(shù):{"uId":"10001","bizId":"791098764902132","awardNumber":"EGM1023938910232121323432","awardType":1}
      22:17:55.674 [main] INFO  org.itstack.demo.test.ApiTest - 測(cè)試結(jié)果:{"code":"0000","info":"發(fā)放成功"}
      
      模擬方法實(shí)物商品
      
      22:17:55.675 [main] INFO  o.i.demo.design.PrizeController - 獎(jiǎng)品發(fā)放開始10001。req:{"awardNumber":"9820198721311","awardType":2,"bizId":"1023000020112221113","extMap":{"consigneeUserName":"謝飛機(jī)","consigneeUserPhone":"15200292123","consigneeUserAddress":"吉林省.長(zhǎng)春市.雙陽(yáng)區(qū).XX街道.檀溪苑小區(qū).#18-2109"},"uId":"10001"}
      模擬發(fā)貨實(shí)物商品一個(gè):{"consigneeUserAddress":"吉林省.長(zhǎng)春市.雙陽(yáng)區(qū).XX街道.檀溪苑小區(qū).#18-2109","consigneeUserName":"謝飛機(jī)","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"}
      22:17:55.677 [main] INFO  o.i.demo.design.PrizeController - 獎(jiǎng)品發(fā)放完成1000122:17:55.677 [main] INFO  org.itstack.demo.test.ApiTest - 請(qǐng)求參數(shù):{"extMap":{"consigneeUserName":"謝飛機(jī)","consigneeUserAddress":"吉林省.長(zhǎng)春市.雙陽(yáng)區(qū).XX街道.檀溪苑小區(qū).#18-2109","consigneeUserPhone":"15200292123"},"uId":"10001","bizId":"1023000020112221113","awardNumber":"9820198721311","awardType":2}
      22:17:55.677 [main] INFO  org.itstack.demo.test.ApiTest - 測(cè)試結(jié)果:{"code":"0000","info":"發(fā)放成功"}
      
      第三方兌換卡(愛奇藝)
      
      22:17:55.678 [main] INFO  o.i.demo.design.PrizeController - 獎(jiǎng)品發(fā)放開始10001。req:{"awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3,"uId":"10001"}
      模擬發(fā)放愛奇藝會(huì)員卡一張:15200101232,AQY1xjkUodl8LO975GdfrYUio
      22:17:55.678 [main] INFO  o.i.demo.design.PrizeController - 獎(jiǎng)品發(fā)放完成10001。
      22:17:55.678 [main] INFO  org.itstack.demo.test.ApiTest - 請(qǐng)求參數(shù):{"uId":"10001","awardNumber":"AQY1xjkUodl8LO975GdfrYUio","awardType":3}
      22:17:55.678 [main] INFO  org.itstack.demo.test.ApiTest - 測(cè)試結(jié)果:{"code":"0000","info":"發(fā)放成功"}
      
      Process finished with exit code 0
      
      • 運(yùn)行結(jié)果正常,滿足當(dāng)前所有業(yè)務(wù)產(chǎn)品需求,寫的還很快。但!實(shí)在難以為維護(hù)!

      五、工廠模式優(yōu)化代碼

      接下來(lái)使用工廠方法模式來(lái)進(jìn)行代碼優(yōu)化,也算是一次很小的重構(gòu)。整理重構(gòu)會(huì)你會(huì)發(fā)現(xiàn)代碼結(jié)構(gòu)清晰了、也具備了下次新增業(yè)務(wù)需求的擴(kuò)展性。但在實(shí)際使用中還會(huì)對(duì)此進(jìn)行完善,目前的只是抽離出最核心的部分體現(xiàn)到你面前,方便學(xué)習(xí)。

      1. 工程結(jié)構(gòu)

      itstack-demo-design-1-02
      └── src
          ├── main
          │   └── java
          │       └── org.itstack.demo.design
          │           ├── store    
          │           │   ├── impl
          │           │   │   ├── CardCommodityService.java
          │           │   │   ├── CouponCommodityService.java 
          │           │   │   └── GoodsCommodityService.java  
          │           │   └── ICommodity.java
          │           └── StoreFactory.java 
          └── test
               └── java
                   └── org.itstack.demo.design.test
                       └── ApiTest.java
      
      • 首先,從上面的工程結(jié)構(gòu)中你是否一些感覺,比如;它看上去清晰了、這樣分層可以更好擴(kuò)展了、似乎可以想象到每一個(gè)類做了什么。
      • 如果還不能理解為什么這樣修改,也沒有關(guān)系。因?yàn)槟闶窃谕ㄟ^(guò)這樣的文章,來(lái)學(xué)習(xí)設(shè)計(jì)模式的魅力。并且再獲取源碼后,進(jìn)行實(shí)際操作幾次也就慢慢掌握了工廠模式的技巧。

      2. 代碼實(shí)現(xiàn)

      2.1 定義發(fā)獎(jiǎng)接口

      public interface ICommodity {
      
          void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;
      
      }
      
      • 所有的獎(jiǎng)品無(wú)論是實(shí)物、虛擬還是第三方,都需要通過(guò)我們的程序?qū)崿F(xiàn)此接口進(jìn)行處理,以保證最終入?yún)⒊鰠⒌慕y(tǒng)一性。
      • 接口的入?yún)?#xff1b;用戶ID、獎(jiǎng)品ID業(yè)務(wù)ID以及擴(kuò)展字段用于處理發(fā)放實(shí)物商品時(shí)的收獲地址。

      2.2 實(shí)現(xiàn)獎(jiǎng)品發(fā)放接口

      優(yōu)惠券

      public class CouponCommodityService implements ICommodity {
      
          private Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);
      
          private CouponService couponService = new CouponService();
      
          public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
              CouponResult couponResult = couponService.sendCoupon(uId, commodityId, bizId);
              logger.info("請(qǐng)求參數(shù)[優(yōu)惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
              logger.info("測(cè)試結(jié)果[優(yōu)惠券]:{}", JSON.toJSON(couponResult));
              if (!"0000".equals(couponResult.getCode())) throw new RuntimeException(couponResult.getInfo());
          }
      
      }
      

      實(shí)物商品

      public class GoodsCommodityService implements ICommodity {
      
          private Logger logger = LoggerFactory.getLogger(GoodsCommodityService.class);
      
          private GoodsService goodsService = new GoodsService();
      
          public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
              DeliverReq deliverReq = new DeliverReq();
              deliverReq.setUserName(queryUserName(uId));
              deliverReq.setUserPhone(queryUserPhoneNumber(uId));
              deliverReq.setSku(commodityId);
              deliverReq.setOrderId(bizId);
              deliverReq.setConsigneeUserName(extMap.get("consigneeUserName"));
              deliverReq.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));
              deliverReq.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));
      
              Boolean isSuccess = goodsService.deliverGoods(deliverReq);
      
              logger.info("請(qǐng)求參數(shù)[優(yōu)惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
              logger.info("測(cè)試結(jié)果[優(yōu)惠券]:{}", isSuccess);
      
              if (!isSuccess) throw new RuntimeException("實(shí)物商品發(fā)放失敗");
          }
      
          private String queryUserName(String uId) {
              return "花花";
          }
      
          private String queryUserPhoneNumber(String uId) {
              return "15200101232";
          }
      
      }
      

      第三方兌換卡

      public class CardCommodityService implements ICommodity {
      
          private Logger logger = LoggerFactory.getLogger(CardCommodityService.class);
      
          // 模擬注入
          private IQiYiCardService iQiYiCardService = new IQiYiCardService();
      
          public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {
              String mobile = queryUserMobile(uId);
              iQiYiCardService.grantToken(mobile, bizId);
              logger.info("請(qǐng)求參數(shù)[愛奇藝兌換卡] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));
              logger.info("測(cè)試結(jié)果[愛奇藝兌換卡]:success");
          }
      
          private String queryUserMobile(String uId) {
              return "15200101232";
          }
      
      }
      
      • 從上面可以看到每一種獎(jiǎng)品的實(shí)現(xiàn)都包括在自己的類中,新增、修改或者刪除都不會(huì)影響其他獎(jiǎng)品功能的測(cè)試,降低回歸測(cè)試的可能。
      • 后續(xù)在新增的獎(jiǎng)品只需要按照此結(jié)構(gòu)進(jìn)行填充即可,非常易于維護(hù)和擴(kuò)展。
      • 在統(tǒng)一了入?yún)⒁约俺鰠⒑?#xff0c;調(diào)用方不在需要關(guān)心獎(jiǎng)品發(fā)放的內(nèi)部邏輯,按照統(tǒng)一的方式即可處理。

      2.3 創(chuàng)建商店工廠

      public class StoreFactory {
      
          public ICommodity getCommodityService(Integer commodityType) {
              if (null == commodityType) return null;
              if (1 == commodityType) return new CouponCommodityService();
              if (2 == commodityType) return new GoodsCommodityService();
              if (3 == commodityType) return new CardCommodityService();
              throw new RuntimeException("不存在的商品服務(wù)類型");
          }
      
      }
      
      • 這里我們定義了一個(gè)商店的工廠類,在里面按照類型實(shí)現(xiàn)各種商品的服務(wù)??梢苑浅8蓛粽麧嵉奶幚砟愕拇a,后續(xù)新增的商品在這里擴(kuò)展即可。如果你不喜歡if判斷,也可以使用switch或者map配置結(jié)構(gòu),會(huì)讓代碼更加干凈。
      • 另外很多代碼檢查軟件和編碼要求,不喜歡if語(yǔ)句后面不寫擴(kuò)展,這里是為了更加干凈的向你體現(xiàn)邏輯。在實(shí)際的業(yè)務(wù)編碼中可以添加括號(hào)。

      3. 測(cè)試驗(yàn)證

      編寫測(cè)試類:

      @Test
      public void test_commodity() throws Exception {
          StoreFactory storeFactory = new StoreFactory();
          // 1. 優(yōu)惠券
          ICommodity commodityService_1 = storeFactory.getCommodityService(1);
          commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);
          // 2. 實(shí)物商品
          ICommodity commodityService_2 = storeFactory.getCommodityService(2);
          
          Map<String,String> extMap = new HashMap<String,String>();
          extMap.put("consigneeUserName", "謝飛機(jī)");
          extMap.put("consigneeUserPhone", "15200292123");
          extMap.put("consigneeUserAddress", "吉林省.長(zhǎng)春市.雙陽(yáng)區(qū).XX街道.檀溪苑小區(qū).#18-2109");
      
          commodityService_2.sendCommodity("10001","9820198721311","1023000020112221113", extMap);
          // 3. 第三方兌換卡(愛奇藝)
          ICommodity commodityService_3 = storeFactory.getCommodityService(3);
          commodityService_3.sendCommodity("10001","AQY1xjkUodl8LO975GdfrYUio",null,null);
      }
      

      結(jié)果:

      模擬發(fā)放優(yōu)惠券一張:10001,EGM1023938910232121323432,791098764902132
      22:48:10.922 [main] INFO  o.i.d.d.s.i.CouponCommodityService - 請(qǐng)求參數(shù)[優(yōu)惠券] => uId:10001 commodityId:EGM1023938910232121323432 bizId:791098764902132 extMap:null
      22:48:10.957 [main] INFO  o.i.d.d.s.i.CouponCommodityService - 測(cè)試結(jié)果[優(yōu)惠券]{"code":"0000","info":"發(fā)放成功"}
      模擬發(fā)貨實(shí)物商品一個(gè):{"consigneeUserAddress":"吉林省.長(zhǎng)春市.雙陽(yáng)區(qū).XX街道.檀溪苑小區(qū).#18-2109","consigneeUserName":"謝飛機(jī)","consigneeUserPhone":"15200292123","orderId":"1023000020112221113","sku":"9820198721311","userName":"花花","userPhone":"15200101232"}
      22:48:10.962 [main] INFO  o.i.d.d.s.impl.GoodsCommodityService - 請(qǐng)求參數(shù)[優(yōu)惠券] => uId:10001 commodityId:9820198721311 bizId:1023000020112221113 extMap:{"consigneeUserName":"謝飛機(jī)","consigneeUserAddress":"吉林省.長(zhǎng)春市.雙陽(yáng)區(qū).XX街道.檀溪苑小區(qū).#18-2109","consigneeUserPhone":"15200292123"}
      22:48:10.962 [main] INFO  o.i.d.d.s.impl.GoodsCommodityService - 測(cè)試結(jié)果[優(yōu)惠券]true
      模擬發(fā)放愛奇藝會(huì)員卡一張:15200101232,null
      22:48:10.963 [main] INFO  o.i.d.d.s.impl.CardCommodityService - 請(qǐng)求參數(shù)[愛奇藝兌換卡] => uId:10001 commodityId:AQY1xjkUodl8LO975GdfrYUio bizId:null extMap:null
      22:48:10.963 [main] INFO  o.i.d.d.s.impl.CardCommodityService - 測(cè)試結(jié)果[愛奇藝兌換卡]:success
      
      Process finished with exit code 0
      
      • 運(yùn)行結(jié)果正常,既滿足了業(yè)務(wù)產(chǎn)品需求,也滿足了自己對(duì)代碼的追求。這樣的代碼部署上線運(yùn)行,內(nèi)心不會(huì)恐慌,不會(huì)覺得半夜會(huì)有電話。
      • 另外從運(yùn)行測(cè)試結(jié)果上也可以看出來(lái),在進(jìn)行封裝后可以非常清晰的看到一整套發(fā)放獎(jiǎng)品服務(wù)的完整性,統(tǒng)一了入?yún)?、統(tǒng)一了結(jié)果。

      六、總結(jié)

      • 從上到下的優(yōu)化來(lái)看,工廠方法模式并不復(fù)雜,甚至這樣的開發(fā)結(jié)構(gòu)在你有所理解后,會(huì)發(fā)現(xiàn)更加簡(jiǎn)單了。
      • 那么這樣的開發(fā)的好處知道后,也可以總結(jié)出來(lái)它的優(yōu)點(diǎn);避免創(chuàng)建者與具體的產(chǎn)品邏輯耦合、滿足單一職責(zé),每一個(gè)業(yè)務(wù)邏輯實(shí)現(xiàn)都在所屬自己的類中完成、滿足開閉原則,無(wú)需更改使用調(diào)用方就可以在程序中引入新的產(chǎn)品類型。但這樣也會(huì)帶來(lái)一些問(wèn)題,比如有非常多的獎(jiǎng)品類型,那么實(shí)現(xiàn)的子類會(huì)極速擴(kuò)張。因此也需要使用其他的模式進(jìn)行優(yōu)化,這些在后續(xù)的設(shè)計(jì)模式中會(huì)逐步涉及到。
      • 從案例入手看設(shè)計(jì)模式往往要比看理論學(xué)的更加容易,因?yàn)榘咐强s短理論到上手的最佳方式,如果你已經(jīng)有所收獲,一定要去嘗試實(shí)操。

      七、往期推薦

      • Java開發(fā)架構(gòu)篇:初識(shí)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD落地
      • Java開發(fā)架構(gòu)篇:DDD模型領(lǐng)域?qū)記Q策規(guī)則樹服務(wù)設(shè)計(jì)
      • Java開發(fā)架構(gòu)篇:領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)架構(gòu)基于SpringCloud搭建微服務(wù)
      • 11 萬(wàn)字的字節(jié)碼編程系列合集放送

      八、彩蛋

      CodeGuide | 程序員編碼指南 Go!

      本代碼庫(kù)是作者小傅哥多年從事一線互聯(lián)網(wǎng) Java 開發(fā)的學(xué)習(xí)歷程技術(shù)匯總,旨在為大家提供一個(gè)清晰詳細(xì)的學(xué)習(xí)教程,側(cè)重點(diǎn)更傾向編寫Java核心內(nèi)容。如果本倉(cāng)庫(kù)能為您提供幫助,請(qǐng)給予支持(關(guān)注、點(diǎn)贊、分享)!

      CodeGuide | 程序員編碼指南

        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多