1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        一個Go方法,五種變換

        共 4781字,需瀏覽 10分鐘

         ·

        2021-11-19 09:43

        今天帶大家來一段代碼的變換之旅,代碼篇幅較多,主要參考自one-way-to-do-it-six-variations,土撥鼠在這里加了一些自己的理解。

        文章: https://phlatphrog.medium.com/one-way-to-do-it-six-variations-cd58602ac06d

        代碼倉庫: https://github.com/pdk/oneway

        我們先來模擬一段開車去商場購物的代碼。主要經(jīng)歷三個階段:開車去商店、到商店購物、然后購物完成開車回家。每個過程都需要去check其中返回的err錯誤并返回shopper對象。

        //?開車去商店?
        shopper,?err?:=?shopper.Drive(FuelNeededToGetToStore)
        if?nil?!=?err?{
        ????log.Fatalf("could?not?complete?shopping:?%s",?err)
        }
        //?買雞蛋?
        shopper,?err?=?shopper.BuyEggs(EggsRequired)
        if?nil?!=?err?{
        ????log.Fatalf("could?not?complete?shopping:?%s",?err)
        }
        //?買完雞蛋開車回家?
        shopper,?err?=?shopper.Drive(FuelNeededToGetHome)
        if?nil?!=?err?{
        ????log.Fatalf("could?not?complete?shopping:?%s",?err)
        }

        變換1:err集中判斷

        針對這段代碼,錯誤處理代碼較多,可以將三個重復(fù)代碼塊抽離成一個代碼塊。代碼如下:

        func?main(){
        ????shopper,?err?:=?shopper.Drive(FuelNeededToGetToStore)
        ????FatalIfErrNotNil(err)
        ????shopper,?err?=?shopper.BuyEggs(EggsRequired)
        ????FatalIfErrNotNil(err)
        ????shopper,?err?=?shopper.Drive(FuelNeededToGetHome)
        ????FatalIfErrNotNil(err)
        }??

        func?FatalIfErrNotNil(err?error)?{
        ????if?nil?!=?err?{
        ????????log.Fatalf("could?not?complete?shopping:?%s",?err)
        ????}
        }

        不過在生產(chǎn)項目中,正常業(yè)務(wù)我們一般不會使用fatal去對非nilerr處理,fatal會以非0狀態(tài)退出程序。而是使用https://github.com/pkg/errors對err進行wrapcause 或者使用定義的業(yè)務(wù)相關(guān)的err并返回上一層做處理。這里也可以嘗試使用bool判斷來對err處理(不過之前土撥鼠之前群里也提出這樣的想法,大家都說多此一舉,尷尬)。

        func?main(){
        ????shopper,?err?:=?shopper.Drive(FuelNeededToGetToStore)
        ????if?ErrorHandled(err)?{
        ??????return?...
        ????}
        }

        func?ErrorHandled(err)?bool?{
        ??if?nil?!=?err?{
        ????return?true
        ??}
        ??//?也可以對error進行其他判斷操作
        ??...
        ??return?false
        }

        變換2:方法中包括err處理

        是不是覺得上面的err處理還是不夠優(yōu)雅,所以咱們這里考慮把err的處理移出到主線之外。每個方法里都包含了對err的非nil判斷和返回。每個操作也只有在errnil的情況下正常執(zhí)行 。只有在三個操作都結(jié)束后才真正對err進行處理。

        func?main(){
        ??shopper,?err?:=?shopper.Drive(FuelNeededToGetToStore,?nil)
        ????shopper,?err?=?shopper.BuyEggs(EggsRequired,?err)
        ????shopper,?err?=?shopper.Drive(FuelNeededToGetHome,?err)
        ????if?nil?!=?err?{
        ????????log.Fatalf("could?not?complete?shopping:?%s",?err)
        ????}
        }

        func?(s?Shopper)?Drive(fuelRequired?int,?err?error)?(Shopper,?error)?{
        ????if?nil?!=?err?{
        ????????return?s,?err
        ????}
        ???//?業(yè)務(wù)邏輯處理
        ????...?
        }

        變換3:使用函數(shù)對err分解

        下面這個變換,拋棄了之前Shopper作為receiver的做法,而是將Shopper作為函數(shù)的參數(shù)。其實跟變換2很相似。這兒是單獨抽離出一個公共的函數(shù)ErrCheckFunc進行err處理和函數(shù)執(zhí)行。

        func?main(){
        ????drive?:=?ErrCheckFunc(Drive)
        ????buy?:=?ErrCheckFunc(BuyEggs)
        ????err,?shopper?:=?drive(nil,?shopper,?FuelNeededToGetToStore)
        ????err,?shopper?=?buy(err,?shopper,?EggsRequired)
        ????err,?shopper?=?drive(err,?shopper,?FuelNeededToGetHome)
        ????if?nil?!=?err?{
        ????????log.Fatalf("could?not?complete?shopping:?%s",?err)
        ????}
        }

        func?Drive(s?Shopper,?fuelRequired?int)?(Shopper,?error)?{
        ????if?nil?!=?err?{
        ????????return?s,?err
        ????}
        ???...
        }

        func?ErrCheckFunc(f?func(Shopper,?int)?(Shopper,?error))?func(error,?Shopper,?int)?(error,?Shopper)?{
        ????return?func(err?error,?s?Shopper,?arg?int)?(error,?Shopper)?{
        ????????if?nil?!=?err?{
        ????????????return?err,?s
        ????????}
        ????????s,?err?=?f(s,?arg)
        ????????return?err,?s
        ????}
        }

        變換4:單行操作

        這次改進是采用了裝飾器模式的思想把errcheck邏輯和函數(shù)的執(zhí)行操作都封裝在了ErrCheckFunc一個方法中。相比變換3,是將ErrCheckFunc中的返回值中的函數(shù)入?yún)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">arg轉(zhuǎn)移到了ErrCheckFunc入?yún)⒅械?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">arg參數(shù)。

        func?main(){
        ????driveToStore?:=?ErrCheckFunc(Drive,?FuelNeededToGetToStore)
        ????buyEggs?:=?ErrCheckFunc(BuyEggs,?EggsRequired)
        ????driveHome?:=?ErrCheckFunc(Drive,?FuelNeededToGetHome)
        ??
        ????err,?shopper?:=?driveHome(buyEggs(driveToStore(nil,?shopper)))
        ????if?nil?!=?err?{
        ????????log.Fatalf("could?not?complete?shopping:?%s",?err)
        ????}
        }

        func?ErrCheckFunc(f?func(Shopper,?int)?(Shopper,?error),?arg?int)?func(error,?Shopper)?(error,?Shopper)?{
        ????return?func(err?error,?s?Shopper)?(error,?Shopper)?{
        ????????if?nil?!=?err?{
        ????????????return?err,?s
        ????????}
        ????????s,?err?=?f(s,?arg)
        ????????return?err,?s
        ????}
        }

        變換5:迭代變換

        終極變換來了,這次變換采用了迭代的思想。ProcessSteps這里使用的是一個可變的函數(shù)參數(shù)。

        func?main(){
        ????driveToStore?:=?Flavorize(Drive,?FuelNeededToGetToStore)
        ????buyEggs?:=?Flavorize(BuyEggs,?EggsRequired)
        ????driveHome?:=?Flavorize(Drive,?FuelNeededToGetHome)
        ??
        ????shopper,?err?:=?ProcessSteps(shopper,
        ????????driveToStore,
        ????????buyEggs,
        ????????driveHome,
        ????)
        ????if?nil?!=?err?{
        ????????log.Fatalf("could?not?complete?shopping:?%s",?err)
        ????}
        }

        func?ProcessSteps(s?Shopper,?steps?...func(Shopper)?(Shopper,?error))?(Shopper,?error)?{
        ????for?_,?step?:=?range?steps?{
        ????????var?err?error?
        ????????s,?err?=?step(s)
        ????????if?nil?!=?err?{
        ????????????return?s,?err
        ????????}
        ????}
        ????return?s,?nil
        }

        func?Flavorize(f?func(Shopper,?int)?(Shopper,?error),?arg?int)?func(Shopper)?(Shopper,?error)?{
        ?return?func(s?Shopper)?(Shopper,?error)?{
        ??return?f(s,?arg)
        ?}
        }

        小結(jié)

        土撥鼠今天拿這個例子來展示給大家,主要是因為在項目中也會經(jīng)常遇到這樣的代碼邏輯場景,就想著看看別人是怎么去重構(gòu)改進的,學(xué)習(xí)怎么去優(yōu)化重構(gòu),使代碼變得更好維護、易于擴展、復(fù)用性高、可讀性高。希望大家也可以借鑒學(xué)習(xí),如果你有更好的變換和想法歡迎大家留言討論。



        推薦閱讀


        福利

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

        瀏覽 24
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            婷婷综合在线 | 色色日韩| 中文字幕在线观看亚洲 | 好骚综合在线 | 啊灬好用力灬嗯灬h视频 | 我要草逼网 | 美女啪啪视频在线观看 | 亚洲中文字幕无码久久2021 | 日本三级电影观看 | 成人视频高清无码在线观看 |