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

          多圖詳解:七種具體方法增強代碼可擴展性

          共 9637字,需瀏覽 20分鐘

           ·

          2022-05-13 01:37


          JAVA前線?


          歡迎大家關(guān)注公眾號「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)



          1 六大原則

          在設(shè)計模式中有六大設(shè)計原則:

          單一職責(zé)原則:一次做一件事

          里式替換原則:子類擴展父類

          依賴倒置原則:面向接口編程

          接口隔離原則:高內(nèi)聚低耦合

          迪米特法則:最少知道原則

          開閉原則:關(guān)閉修改,開放新增

          我認(rèn)為在這六個原則中開閉原則最為重要,開閉原則是可擴展性的重要基石。

          第一個原因是需求變化時應(yīng)該通過新增而不是修改已有代碼實現(xiàn),這樣保證了代碼穩(wěn)定性,避免牽一發(fā)而動全身。

          第二個原因是可以事先定義代碼框架,擴展也是根據(jù)框架擴展,體現(xiàn)了用抽象構(gòu)建框架,用實現(xiàn)擴展細(xì)節(jié),保證了穩(wěn)定性也保證了靈活性。

          第三個原因是其它五個原則雖然側(cè)重點各有不同,但是都可以包含于開閉原則。

          第四個原因是標(biāo)準(zhǔn)二十三種設(shè)計模式最終都是在遵循開閉原則。

          既然開閉原則如此重要,我們應(yīng)該怎么在系統(tǒng)設(shè)計時遵循開閉原則呢?


          2 數(shù)據(jù)庫維度

          2.1 設(shè)計類型字段

          我們可以在數(shù)據(jù)表設(shè)計時新增兩列:biz_type、biz_sub_type,這兩個列即使目前用不上,可以先設(shè)置一個默認(rèn)值。

          biz_type可以指業(yè)務(wù)類型,業(yè)務(wù)渠道、租戶等等,歸根結(jié)底是用來隔離大業(yè)務(wù)類型,假設(shè)后續(xù)業(yè)務(wù)新增了一個大業(yè)務(wù)類型,那么可以通過這個字段隔離。

          biz_sub_type可以在大業(yè)務(wù)類型中細(xì)分小業(yè)務(wù),使得業(yè)務(wù)更加細(xì)化,靈活性更好。


          2.2 設(shè)計擴展字段

          我們可以在數(shù)據(jù)表設(shè)計時新增三列:extend1、extend2、extend3,可以先設(shè)置為空。擴展字段存儲JSON類型信息,存放一些擴展信息、附加信息、松散信息或者是之前未預(yù)估到的信息。

          之所以設(shè)置三個擴展字段是為了增加隔離性,例如extend1存放訂單擴展信息,extend2存放商品擴展信息,extend3存放營銷擴展信息。


          2.3 設(shè)計業(yè)務(wù)二進(jìn)制字段

          2.3.1 需求背景

          我們可以在數(shù)據(jù)表設(shè)計時新增業(yè)務(wù)二進(jìn)制字段,這個字段可以很大程度上擴展業(yè)務(wù)表意能力。假設(shè)在系統(tǒng)中用戶一共有三種角色:普通用戶、管理員、超級管理員,現(xiàn)在需要設(shè)計一張用戶角色表記錄這類信息。不難設(shè)計出如下方案:

          idnamesuperadminnormal
          101用戶一100
          102用戶二010
          103用戶三001
          104用戶四111

          觀察上表不難得出,用戶一具有超級管理員角色,用戶二具有管理員角色,用戶三具有普通用戶角色,用戶四同時具有三種角色。如果此時新增加一種角色呢?那么新增一個字段即可。


          2.3.2 發(fā)現(xiàn)問題

          按照上述一個字段表示一種角色進(jìn)行表設(shè)計功能上是沒有問題的,優(yōu)點是容易理解結(jié)構(gòu)清晰,但是我們想一想有沒有什么問題?筆者遇到過如下問題:在復(fù)雜業(yè)務(wù)環(huán)境一份數(shù)據(jù)可能會使用在不同的場景,例如上述數(shù)據(jù)存儲在MySQL數(shù)據(jù)庫,這一份數(shù)據(jù)還會被用在如下場景:

          檢索數(shù)據(jù)需要同步一份到ES

          業(yè)務(wù)方使用此表通過Flink計算業(yè)務(wù)指標(biāo)

          業(yè)務(wù)方訂閱此表Binlog消息進(jìn)行業(yè)務(wù)處理

          如果表結(jié)構(gòu)發(fā)生變化,數(shù)據(jù)源之間就要重新進(jìn)行對接,業(yè)務(wù)方也要進(jìn)行代碼修改,這樣開發(fā)成本比較非常高。有沒有辦法避免此類問題?


          2.3.3 解決方案

          我們可以使用位圖法,這樣同一個字段可以表示多個業(yè)務(wù)含義。首先設(shè)計如下數(shù)據(jù)表,userFlag字段暫時不填。

          idnameuser_flag
          101用戶一暫時不填
          102用戶二暫時不填
          103用戶三暫時不填
          104用戶四暫時不填

          設(shè)計位圖每一個bit表示一種角色:



          使用位圖法表示如下數(shù)據(jù)表:

          idnamesuperadminnormal
          101用戶一100
          102用戶二010
          103用戶三001
          104用戶四111

          用戶一位圖如下其十進(jìn)制數(shù)值等于4:


          用戶二位圖如下其十進(jìn)制數(shù)值等于2:


          用戶三位圖如下其十進(jìn)制數(shù)值等于1:


          用戶四位圖如下其十進(jìn)制數(shù)值等于7:


          根據(jù)上述分析可以填寫數(shù)據(jù)表第三列:

          idnameuser_flag
          101用戶一4
          102用戶二2
          103用戶三1
          104用戶四7

          2.3.4 代碼實例

          定義枚舉時不要直接定義為1、2、4這類數(shù)字,而應(yīng)該采用位移方式進(jìn)行定義,這樣使用者可以明白設(shè)計者的意圖。

          /**
          ?*?用戶角色枚舉
          ?*
          ?*?@author?微信公眾號「JAVA前線」
          ?*
          ?*/

          public?enum?UserRoleEnum?{

          ????//?1?->?00000001
          ????NORMAL(1,?"普通用戶"),

          ????//?2?->?00000010
          ????MANAGER(1?<1,?"管理員"),

          ????//?4?->?00000100
          ????SUPER(1?<2,?"超級管理員")

          ????;

          ????private?int?code;
          ????private?String?description;

          ????private?UserRoleEnum(Integer?code,?String?description)?{
          ????????this.code?=?code;
          ????????this.description?=?description;
          ????}

          ????public?String?getDescription()?{
          ????????return?description;
          ????}

          ????public?int?getCode()?{
          ????????return?this.code;
          ????}
          }

          假設(shè)用戶已經(jīng)具有普通用戶角色,我們需要為其增加管理員角色,這就是新增角色,與之對應(yīng)還有刪除角色和查詢角色,這些操作需要用到為位運算,詳見代碼注釋。

          /**
          ?*?用戶角色枚舉
          ?*
          ?*?@author?微信公眾號「JAVA前線」
          ?*
          ?*/

          public?enum?UserRoleEnum?{

          ????//?1?->?00000001
          ????NORMAL(1,?"普通用戶"),

          ????//?2?->?00000010
          ????MANAGER(1?<1,?"管理員"),

          ????//?4?->?00000100
          ????SUPER(1?<2,?"超級管理員")

          ????;

          ????//?新增角色?->?位或操作
          ????//?oldRole?->?00000001?->?普通用戶
          ????//?addRole?->?00000010?->?新增管理員
          ????//?newRole?->?00000011?->?普通用戶和管理員
          ????public?static?Integer?addRole(Integer?oldRole,?Integer?addRole)?{
          ????????return?oldRole?|?addRole;
          ????}

          ????//?刪除角色?->?位異或操作
          ????//?oldRole?->?00000011?->?普通用戶和管理員
          ????//?delRole?->?00000010?->?刪除管理員
          ????//?newRole?->?00000001?->?普通用戶
          ????public?static?Integer?removeRole(Integer?oldRole,?Integer?delRole)?{
          ????????return?oldRole?^?delRole;
          ????}

          ????//?是否有某種角色?->?位與操作
          ????//?allRole?->?00000011?->?普通用戶和管理員
          ????//?qryRole?->?00000001?->?是否有管理員角色
          ????//?resRole?->?00000001?->?有普通用戶角色
          ????public?static?boolean?hasRole(Integer?allRole,?Integer?qryRole)?{
          ????????return?qryRole?==?(role?&?qryRole);
          ????}

          ????private?int?code;
          ????private?String?description;

          ????private?UserRoleEnum(Integer?code,?String?description)?{
          ????????this.code?=?code;
          ????????this.description?=?description;
          ????}

          ????public?String?getDescription()?{
          ????????return?description;
          ????}

          ????public?int?getCode()?{
          ????????return?this.code;
          ????}

          ????public?static?void?main(String[]?args)?{
          ????????System.out.println(addRole(1,?2));
          ????????System.out.println(removeRole(3,?1));
          ????????System.out.println(hasRole(3,?1));
          ????}
          }

          假設(shè)在運營后臺查詢界面中,需要查詢具有普通用戶角色的用戶數(shù)據(jù),可以使用如下SQL語句:

          select?*?from?user_role?where?(user_flag?&?1)?=?user_flag;
          select?*?from?user_role?where?(user_flag?&?b'0001')?=?user_flag;

          我們也可以使用MyBatis語句:

          <select?id="selectByUserRole"?resultMap="BaseResultMap"?parameterType="java.util.Map">
          ??select?*?from?user_role?
          ??where?user_flag?&?#{userFlag}?=?#{userFlag}
          select>

          <select?id="selectByUserIdAndRole"?resultMap="BaseResultMap"?parameterType="java.util.Map">
          ??select?*?from?user_role?
          ??where?id?=?#{userId}?and?user_flag?&?#{userFlag}?=?#{userFlag}
          select>

          3 接口維度

          3.1 設(shè)計類型入?yún)?/span>

          接口輸入?yún)?shù)一定要設(shè)計類型入?yún)?,可以與數(shù)據(jù)庫biz_type、biz_sub_type相對應(yīng),也可以自定義類型最終翻譯與數(shù)據(jù)庫類型相對應(yīng)。

          如果一開始不用區(qū)分類型,可以先設(shè)置為默認(rèn)值,但是其核心思想是一定要可以通過類型對輸入?yún)?shù)進(jìn)行區(qū)分。

          public?class?OrderDTO?{
          ????private?Integer?bizType;
          ????private?Integer?bizSubType;
          ????private?Long?amount;
          ????private?String?goodsId;
          }

          public?Response?createOrder(OrderDTO?order);

          3.2 設(shè)計松散入?yún)?/span>

          接口輸入?yún)?shù)可以設(shè)計Map類型松散參數(shù),松散參數(shù)缺點是表意能力弱,優(yōu)點是靈活性強,可以以較低成本新增參數(shù)。

          public?class?OrderDTO?{
          ????private?Integer?bizType;
          ????private?Integer?bizSubType;
          ????private?Long?amount;
          ????private?String?goodsId;
          ????private?Map?params;
          }

          public?Response?createOrder(OrderDTO?order);

          3.3 設(shè)計接口版本號

          對外接口需要設(shè)計版本號,對于同一個接口允許外部業(yè)務(wù)逐漸切到新版本,新老版本接口會共存一段時間,通過版本號區(qū)分。

          /order/1.0/createOrder
          /order/1.1/createOrder

          3.4 縱橫做設(shè)計

          我們分析一個下單場景:當(dāng)前有ABC三種訂單類型:A訂單價格9折,物流最大重量不能超過9公斤,不支持退款。B訂單價格8折,物流最大重量不能超過8公斤,支持退款。C訂單價格7折,物流最大重量不能超過7公斤,支持退款。按照需求字面含義平鋪直敘地寫代碼也并不難。

          public?class?OrderServiceImpl?implements?OrderService?{

          ????@Resource
          ????private?OrderMapper?orderMapper;

          ????@Override
          ????public?void?createOrder(OrderBO?orderBO)?{
          ????????if?(null?==?orderBO)?{
          ????????????throw?new?RuntimeException("參數(shù)異常");
          ????????}
          ????????if?(OrderTypeEnum.isNotValid(orderBO.getType()))?{
          ????????????throw?new?RuntimeException("參數(shù)異常");
          ????????}
          ????????//?A類型訂單
          ????????if?(OrderTypeEnum.A_TYPE.getCode().equals(orderBO.getType()))?{
          ????????????orderBO.setPrice(orderBO.getPrice()?*?0.9);
          ????????????if?(orderBO.getWeight()?>?9)?{
          ????????????????throw?new?RuntimeException("超過物流最大重量");
          ????????????}
          ????????????orderBO.setRefundSupport(Boolean.FALSE);
          ????????}
          ????????//?B類型訂單
          ????????else?if?(OrderTypeEnum.B_TYPE.getCode().equals(orderBO.getType()))?{
          ????????????orderBO.setPrice(orderBO.getPrice()?*?0.8);
          ????????????if?(orderBO.getWeight()?>?8)?{
          ????????????????throw?new?RuntimeException("超過物流最大重量");
          ????????????}
          ????????????orderBO.setRefundSupport(Boolean.TRUE);
          ????????}
          ????????//?C類型訂單
          ????????else?if?(OrderTypeEnum.C_TYPE.getCode().equals(orderBO.getType()))?{
          ????????????orderBO.setPrice(orderBO.getPrice()?*?0.7);
          ????????????if?(orderBO.getWeight()?>?7)?{
          ????????????????throw?new?RuntimeException("超過物流最大重量");
          ????????????}
          ????????????orderBO.setRefundSupport(Boolean.TRUE);
          ????????}
          ????????//?保存數(shù)據(jù)
          ????????OrderDO?orderDO?=?new?OrderDO();
          ????????BeanUtils.copyProperties(orderBO,?orderDO);
          ????????orderMapper.insert(orderDO);
          ????}
          }

          上述代碼從功能上完全可以實現(xiàn)業(yè)務(wù)需求,但是程序員不僅要滿足功能,還需要思考代碼的可維護(hù)性。如果新增一種訂單類型,或者新增一個訂單屬性處理邏輯,那么我們就要在上述邏輯中新增代碼,如果處理不慎就會影響原有邏輯。

          如何改變平鋪直敘的思考方式?這就要為問題分析加上縱向和橫向兩個維度,我選擇使用分析矩陣方法,其中縱向表示策略,橫向表示場景:



          3.4.1 縱向做隔離

          縱向維度表示策略,不同策略在邏輯上和業(yè)務(wù)上應(yīng)該是隔離的,本實例包括優(yōu)惠策略、物流策略和退款策略,策略作為抽象,不同訂單類型去擴展這個抽象,策略模式非常適合這種場景。本文詳細(xì)分析優(yōu)惠策略,物流策略和退款策略同理。

          //?優(yōu)惠策略
          public?interface?DiscountStrategy?{
          ????public?void?discount(OrderBO?orderBO);
          }

          //?A類型優(yōu)惠策略
          @Component
          public?class?TypeADiscountStrategy?implements?DiscountStrategy?{

          ????@Override
          ????public?void?discount(OrderBO?orderBO)?{
          ????????orderBO.setPrice(orderBO.getPrice()?*?0.9);
          ????}
          }

          //?B類型優(yōu)惠策略
          @Component
          public?class?TypeBDiscountStrategy?implements?DiscountStrategy?{

          ????@Override
          ????public?void?discount(OrderBO?orderBO)?{
          ????????orderBO.setPrice(orderBO.getPrice()?*?0.8);
          ????}
          }

          //?C類型優(yōu)惠策略
          @Component
          public?class?TypeCDiscountStrategy?implements?DiscountStrategy?{

          ????@Override
          ????public?void?discount(OrderBO?orderBO)?{
          ????????orderBO.setPrice(orderBO.getPrice()?*?0.7);
          ????}
          }

          //?優(yōu)惠策略工廠
          @Component
          public?class?DiscountStrategyFactory?implements?InitializingBean?{
          ????private?Map?strategyMap?=?new?HashMap<>();

          ????@Resource
          ????private?TypeADiscountStrategy?typeADiscountStrategy;
          ????@Resource
          ????private?TypeBDiscountStrategy?typeBDiscountStrategy;
          ????@Resource
          ????private?TypeCDiscountStrategy?typeCDiscountStrategy;

          ????public?DiscountStrategy?getStrategy(String?type)?{
          ????????return?strategyMap.get(type);
          ????}

          ????@Override
          ????public?void?afterPropertiesSet()?throws?Exception?{
          ????????strategyMap.put(OrderTypeEnum.A_TYPE.getCode(),?typeADiscountStrategy);
          ????????strategyMap.put(OrderTypeEnum.B_TYPE.getCode(),?typeBDiscountStrategy);
          ????????strategyMap.put(OrderTypeEnum.C_TYPE.getCode(),?typeCDiscountStrategy);
          ????}
          }

          //?優(yōu)惠策略執(zhí)行
          @Component
          public?class?DiscountStrategyExecutor?{
          ????private?DiscountStrategyFactory?discountStrategyFactory;

          ????public?void?discount(OrderBO?orderBO)?{
          ????????DiscountStrategy?discountStrategy?=?discountStrategyFactory.getStrategy(orderBO.getType());
          ????????if?(null?==?discountStrategy)?{
          ????????????throw?new?RuntimeException("無優(yōu)惠策略");
          ????????}
          ????????discountStrategy.discount(orderBO);
          ????}
          }

          3.4.2 橫向做編排

          橫向維度表示場景,一種訂單類型在廣義上可以認(rèn)為是一種業(yè)務(wù)場景,在場景中將獨立的策略進(jìn)行串聯(lián),模板方法設(shè)計模式適用于這種場景。

          模板方法模式一般使用抽象類定義算法骨架,同時定義一些抽象方法,這些抽象方法延遲到子類實現(xiàn),這樣子類不僅遵守了算法骨架約定,也實現(xiàn)了自己的算法。既保證了規(guī)約也兼顧靈活性,這就是用抽象構(gòu)建框架,用實現(xiàn)擴展細(xì)節(jié)。

          //?創(chuàng)建訂單服務(wù)
          public?interface?CreateOrderService?{
          ????public?void?createOrder(OrderBO?orderBO);
          }

          //?抽象創(chuàng)建訂單流程
          public?abstract?class?AbstractCreateOrderFlow?{

          ????@Resource
          ????private?OrderMapper?orderMapper;

          ????public?void?createOrder(OrderBO?orderBO)?{
          ????????//?參數(shù)校驗
          ????????if?(null?==?orderBO)?{
          ????????????throw?new?RuntimeException("參數(shù)異常");
          ????????}
          ????????if?(OrderTypeEnum.isNotValid(orderBO.getType()))?{
          ????????????throw?new?RuntimeException("參數(shù)異常");
          ????????}
          ????????//?計算優(yōu)惠
          ????????discount(orderBO);
          ????????//?計算重量
          ????????weighing(orderBO);
          ????????//?退款支持
          ????????supportRefund(orderBO);
          ????????//?保存數(shù)據(jù)
          ????????OrderDO?orderDO?=?new?OrderDO();
          ????????BeanUtils.copyProperties(orderBO,?orderDO);
          ????????orderMapper.insert(orderDO);
          ????}

          ????public?abstract?void?discount(OrderBO?orderBO);

          ????public?abstract?void?weighing(OrderBO?orderBO);

          ????public?abstract?void?supportRefund(OrderBO?orderBO);
          }

          //?實現(xiàn)創(chuàng)建訂單流程
          @Service
          public?class?CreateOrderFlow?extends?AbstractCreateOrderFlow?{

          ????@Resource
          ????private?DiscountStrategyExecutor?discountStrategyExecutor;
          ????@Resource
          ????private?ExpressStrategyExecutor?expressStrategyExecutor;
          ????@Resource
          ????private?RefundStrategyExecutor?refundStrategyExecutor;

          ????@Override
          ????public?void?discount(OrderBO?orderBO)?{
          ????????discountStrategyExecutor.discount(orderBO);
          ????}

          ????@Override
          ????public?void?weighing(OrderBO?orderBO)?{
          ????????expressStrategyExecutor.weighing(orderBO);
          ????}

          ????@Override
          ????public?void?supportRefund(OrderBO?orderBO)?{
          ????????refundStrategyExecutor.supportRefund(orderBO);
          ????}
          }

          4 文章總結(jié)

          本文介紹了設(shè)計類型字段、設(shè)計擴展字段、設(shè)計業(yè)務(wù)二進(jìn)制字段、設(shè)計類型入?yún)ⅰ⒃O(shè)計松散入?yún)?、設(shè)計接口版本號、縱橫做設(shè)計這七種增加系統(tǒng)可擴展性方法,希望本文對大家有所幫助。



          JAVA前線?


          歡迎大家關(guān)注公眾號「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)

          瀏覽 23
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  色哟哟哟 入口国产精品 | 国产在视频一区二区三区吞精 | www.av天天 | 新国产在线观看色情 | 国产肉体ⅩXXX137大胆 |