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

          Go:如何應(yīng)對(duì)不斷膨脹的接口

          共 3005字,需瀏覽 7分鐘

           ·

          2022-01-01 17:46

          難怪碼農(nóng)自嘲是 CRUD boy, 每天確實(shí)在不斷的堆屎,在別人的屎山上縫縫補(bǔ)補(bǔ)。下面的案例并沒有 blame 任何人的意思,我也是堆屎工^^ 如有雷同,請(qǐng)勿對(duì)號(hào)入座

          案例

          最近讀一個(gè)業(yè)務(wù)代碼,狀態(tài)機(jī)接口定義有 40 個(gè)函數(shù),查看 commit log, 初始只有 10 個(gè),每當(dāng)增加新的業(yè)務(wù)需求時(shí),就不斷的在原接口添加

          //?OrderManager?handles?operation?on?order?entity
          type?OrderManager?interface?{
          ?LoadOrdersByIDs(ctx?context.Context,?orderIDs?[]string)?([]*dbentity.Order,?error)
          ??......
          ??TransitOrdersToState(ctx?context.Context,?orderIDs?[]string,?toState?orderstate.OrderState)?([]*dbentity.Order,?error)
          ??......
          ?Stop()?error
          }

          業(yè)務(wù)中很多 interface 都是用來 mock UT, 實(shí)現(xiàn)依賴反轉(zhuǎn),而不是業(yè)務(wù)多態(tài)。OrderManager 就屬于這類,所以接口膨脹后對(duì)工程質(zhì)量影響并不大,就是看著不內(nèi)聚...

          接口為什么要小

          The bigger the interface, the weaker the abstraction.

          Go Proverbs[1] Rob Pike 提到:接口越大,抽像能力越弱,比如系統(tǒng)庫中的 io.Reader, io.Writer 等等接口定義只有一兩個(gè)函數(shù)。為什么說接口要小呢?舉個(gè)例子

          type?FooBeeper?interface?{
          ??Bar(s?string)?(string,?error)
          ??Beep(s?string)?(string,?error)
          }

          type?thing?struct{}

          func?(l?*thing)?Bar(s?string)?(string,?error)?{
          ??...
          }

          func?(l?*thing)?Beep(s?string)?(string,?error)?{
          ??...
          }

          type?differentThing?struct{}

          func?(l?*differentThing)?Bar(s?string)?(string,?error)?{
          ??...
          }

          type?anotherThing?struct{}

          func?(l?*anotherThing)?Beep(s?string)?(string,?error)?{
          ??...
          }

          接口 FooBeeper 定義有兩個(gè)函數(shù): Bar, Beep. 由于接口實(shí)現(xiàn)是隱式的,我們有如下結(jié)論:

          • thing 實(shí)現(xiàn)了 FooBeeper 接口
          • differentThing 沒有實(shí)現(xiàn),缺少 Bar 函數(shù)
          • anotherThing 同樣沒有實(shí)現(xiàn),缺少 Beep 函數(shù)

          但是如果我們把 FooBeeper 打散也多個(gè)接口的組合

          type?FooBeeper?interface?{
          ?Bar
          ?Beep
          }

          type?Bar?interface?{
          ?Bar(s?string)?(string,?error)
          }

          type?Beep?interface?{
          ?Beep(s?string)?(string,?error)
          }

          如上述定義,就可以將接口做小,使得 differentThing anotherThing 可以復(fù)用接口

          組合改造

          關(guān)于如何改造 OrderManger 可以借鑒 etcd client v3[2] 定義的思想,將相關(guān)的功能聚合成小接口,通過接口的組合實(shí)現(xiàn)

          type?Client?struct?{
          ?Cluster
          ?KV
          ?Lease
          ?Watcher
          ?Auth
          ?Maintenance

          ?conn?*grpc.ClientConn

          ?cfg??????Config
          ??......
          }

          上面是 clientV3 結(jié)構(gòu)體定義,雖然不是接口,但是思想可以借鑒

          //?OrderManager?handles?operation?on?order?entity
          type?OrderManager?interface?{
          ?OrderOperator
          ?TransitOrders
          ?Stop()?error
          }

          實(shí)際上可能接口只需抽成三個(gè),OrderOperator 負(fù)責(zé)對(duì) orders 的 CRUD 操作,TransitOrders 負(fù)責(zé)轉(zhuǎn)態(tài)機(jī)流轉(zhuǎn),原來的 40 個(gè)函數(shù)函數(shù)都放到小接口里面

          冗余改造

          只抽成小接口是不行的,LoadOrderByXXXX 有一堆定義,根據(jù)不同條件獲取訂單,但實(shí)際上這些都是可以轉(zhuǎn)換的

          func?LoadOrders(ctx?context.Context,?FiltersParams?options...)

          針對(duì)這種情況可以傳入 option, 或是用結(jié)構(gòu)體當(dāng)成參數(shù)容器。再比如狀態(tài)機(jī)流轉(zhuǎn)有 TransitOrdersToState, TransitOrdersToStateByEntity, TransitOrdersStateByEntityForRegularDelivery 均屬于冗余定義

          還有一種冗余接口是根本沒人用,或是不該這層暴露的

          預(yù)防

          接口拆分本質(zhì)上是 ISP Interface segregation principle[3], 不應(yīng)該強(qiáng)迫任何代碼依賴它不使用的方法IT 行業(yè)有一個(gè)笑話

          當(dāng)你的 MR 只有幾行時(shí),peer 會(huì)提出幾十個(gè) comment. 但是當(dāng)你的 MR 幾百行時(shí),他們只會(huì)回復(fù) LGTM

          Peer review 還是要有責(zé)任心的,如果成本不高,建議順手把老代碼重構(gòu)一下。重構(gòu)代碼有幾項(xiàng)原則,可以參考 重構(gòu)最佳實(shí)踐2

          CI lint 不知道是否支持檢查 interface 行數(shù),但是如果行數(shù)成為指標(biāo),可能又本末倒置了

          小結(jié)

          還是那句話,知易行難,從點(diǎn)滴做起吧

          寫文章不容易,如果對(duì)大家有所幫助和啟發(fā),請(qǐng)大家?guī)兔c(diǎn)擊在看點(diǎn)贊分享 三連

          關(guān)于 接口 大家有什么看法,歡迎留言一起討論,大牛多留言 ^_^

          參考資料

          [1]

          Go Proverbs: https://go-proverbs.github.io/,

          [2]

          etcd client v3: https://github.com/etcd-io/etcd/blob/main/client/v3/client.go#L44,

          [3]

          Interface segregation principle: https://en.wikipedia.org/wiki/Interface_segregation_principle,



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 71
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  亚洲国产精品视频免费看 | 天天干天天日天天色 | 午夜看片 | 欧美成人在线观看免费 | 激情亚洲综合婷婷 |