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

          接口Interface—塑造健壯與可擴(kuò)展的Go應(yīng)用程序

          共 4499字,需瀏覽 9分鐘

           ·

          2020-08-05 00:44

          本文擬以一個(gè)接近實(shí)際的項(xiàng)目需求例子,來(lái)幫助讀者體會(huì)接口使用的重要性,理解Go接口Interface是如何提高項(xiàng)目的魯棒性和擴(kuò)展性。







          場(chǎng)景與接口定義






          場(chǎng)景:假設(shè)有一個(gè)在線商城,需要在Go后臺(tái)提供存儲(chǔ)與查詢產(chǎn)品的服務(wù)。那么我們?cè)陧?xiàng)目中應(yīng)該怎么設(shè)計(jì)該服務(wù)?


          ok,需求很明朗,其實(shí)就是要一個(gè)負(fù)責(zé)保存和檢索產(chǎn)品的存儲(chǔ)庫(kù)。


          package?productrepo

          type?ProductRepository?interface?{
          ?StoreProduct(name?string,?id?int)
          ?FindProductByID(id?int)
          }


          為此,我們創(chuàng)建一個(gè)productrepo的包和一個(gè)api.go的文件。該API應(yīng)該暴露出存儲(chǔ)庫(kù)里所有的產(chǎn)品方法。在productrepo包下,定義了ProductRepository接口,它代表的就是存儲(chǔ)庫(kù)。該接口中我們只定義兩個(gè)簡(jiǎn)單的方法,StoreProduct()方法用于存儲(chǔ)產(chǎn)品信息,FindProductByID()方法通過(guò)產(chǎn)品ID查找產(chǎn)品信息。







          接口實(shí)現(xiàn)示例






          既然已經(jīng)定義了存儲(chǔ)庫(kù)接口,那么現(xiàn)在就需要有實(shí)體對(duì)象去實(shí)現(xiàn)該接口。


          package?productrepo

          import?"fmt"

          type?mockProductRepo?struct?{

          }

          func?(m?mockProductRepo)?StoreProduct(name?string,?id?int)?{
          ?fmt.Println("mocking?the?StoreProduct?func")
          }

          func?(m?mockProductRepo)?FindProductByID(id?int)?{
          ?fmt.Println("mocking?the?FindProductByID?func")
          }


          如上,在productrepo包下,新建mock.go文件,定義了mockProductRepo對(duì)象。正如名字一樣,在示例代碼中我們并不會(huì)真的去做什么(僅僅做個(gè)輸出打?。?,但是會(huì)mock出ProductRepository接口所需的方法。


          這時(shí),在api.go文件中增加一個(gè)方法New(),它返回的一個(gè)實(shí)現(xiàn)了ProductRepository接口的對(duì)象。


          func?New()?ProductRepository?{
          ?return?mockProductRepo{}
          }







          為什么要使用接口?






          對(duì)于我們已經(jīng)定義的ProductRepository接口,可以有多種對(duì)象去實(shí)現(xiàn)它。但是,在最開(kāi)始做開(kāi)發(fā)時(shí),小菜刀對(duì)于接口總是會(huì)很疑惑:為什么要搞個(gè)接口,我就一個(gè)存儲(chǔ)庫(kù)?。ɡ绫镜豈ySQL存儲(chǔ)),何必要這麻煩!


          這種想法,對(duì)于小型的個(gè)人項(xiàng)目來(lái)說(shuō)可能是正確的。但是,事情往往不是這么簡(jiǎn)單。在復(fù)雜的實(shí)際應(yīng)用項(xiàng)目中,我們通常會(huì)有很多種存儲(chǔ)對(duì)象:例如,你可能選擇使用本地MySQL存儲(chǔ),也可能連接到云數(shù)據(jù)庫(kù)(例如阿里云、谷歌云和騰訊云等)存儲(chǔ)。而它們均需要實(shí)現(xiàn)ProductRepository接口定義的StoreProduct()方法和FindProductByID()方法。


          以本地MySQL存儲(chǔ)庫(kù)為例,它要管理產(chǎn)品對(duì)象,需要實(shí)現(xiàn)ProductRepository接口。


          package?productrepo

          import?"fmt"

          type?mysqlProductRepo?struct?{
          }

          func?(m?mysqlProductRepo)?StoreProduct(name?string,?id?int)?{
          ?fmt.Println("mysqlProductRepo:?mocking?the?StoreProduct?func")
          ?//?In?a?real?world?project?you?would?query?a?MySQL?database?here.
          }

          func?(m?mysqlProductRepo)?FindProductByID(id?int)?{
          ?fmt.Println("mysqlProductRepo:?mocking?the?FindProductByID?func")
          ?//?In?a?real?world?project?you?would?query?a?MySQL?database?here.
          }


          如上,在productrepo包下,新建mysql.go文件,定義了mysqlProductRepo對(duì)象并實(shí)現(xiàn)接口方法。


          相似地,當(dāng)項(xiàng)目中同時(shí)需要把產(chǎn)品信息存儲(chǔ)到云端時(shí),以阿里云為例,在productrepo包下,新建aliyun.go文件,定義了aliCloudProductRepo對(duì)象并實(shí)現(xiàn)接口方法。


          package?productrepo

          import?"fmt"

          type?aliCloudProductRepo?struct?{

          }

          func?(m?aliCloudProductRepo)?StoreProduct(name?string,?id?int)?{
          ?fmt.Println("aliCloudProductRepo:?mocking?the?StoreProduct?func")
          ?//?In?a?real?world?project?you?would?query?an?ali?Cloud?database?here.
          }

          func?(m?aliCloudProductRepo)?FindProductByID(id?int)?{
          ?fmt.Println("aliCloudProductRepo:?mocking?the?FindProductByID?func")
          ?//?In?a?real?world?project?you?would?query?an?ali?Cloud?database?here.
          }


          此時(shí),更新前面提到的api.go中定義的New()方法。


          func?New(environment?string)?ProductRepository?{
          ?switch?environment?{
          ?case?"aliCloud":
          ??return?aliCloudProductRepo{}
          ?case?"local-mysql":
          ??return?mysqlProductRepo{}
          ?}

          ?return?mockProductRepo{}
          }


          通過(guò)將環(huán)境變量environment傳遞給New()函數(shù),它將基于該環(huán)境值返回ProductRepository接口的正確實(shí)現(xiàn)對(duì)象。


          定義程序入口main.go文件以及main函數(shù)。


          package?main

          import?"workspace/example/example/productrepo"

          func?main()?{
          ?env?:=?"aliCloud"
          ?repo?:=?productrepo.New(env)
          ?repo.StoreProduct("HuaWei?mate?40",?105)
          }


          這里,通過(guò)使用productrepo.New()方法基于環(huán)境值來(lái)獲取ProductRepository接口對(duì)象。如果你需要切換產(chǎn)品存儲(chǔ)庫(kù),則只需要使用對(duì)應(yīng)的env值調(diào)用productrepo.New()方法即可。


          最終,本文的代碼結(jié)構(gòu)如下


          .
          ├──?go.mod
          ├──?main.go
          └──?productrepo
          ????├──?aliyun.go
          ????├──?api.go
          ????├──?mock.go
          ????└──?mysql.go


          運(yùn)行main.go,結(jié)果如


          $?go?run?main.go
          aliCloudProductRepo:?mocking?the?StoreProduct?func


          如果沒(méi)有接口,要實(shí)現(xiàn)上述main函數(shù)中的調(diào)用,需要增加多少代碼?






          //?1. 需要為每個(gè)對(duì)象增加初始化方法

          • msql.go中增加NewMysqlProductRepo()方法


          func?NewMysqlProductRepo()?*mysqlProductRepo?{
          ?return?&mysqlProductRepo{}
          }


          • aliyun.go中增加NewAliCloudProductRepo()方法


          func?NewAliCloudProductRepo()??*aliCloudProductRepo{
          ?return?&aliCloudProductRepo{}
          }


          • mock.go中增加NewMockProductRepo()方法


          func?NewMockProductRepo()?*mockProductRepo?{
          ?return?&mockProductRepo{}
          }


          //?2.??調(diào)用對(duì)象處產(chǎn)生大量重復(fù)代碼

          package?main

          import?"workspace/example/example/productrepo"

          func?main()?{
          ?env?:=?"aliCloud"
          ?switch?env?{
          ?case?"aliCloud":
          ??repo?:=?productrepo.NewAliCloudProductRepo()
          ??repo.StoreProduct("HuaWei?mate?40",?105)
          ????//?the?more?function?to?do,?the?more?code?is?repeated.
          ?case?"local-mysql":
          ??repo?:=?productrepo.NewMysqlProductRepo()
          ??repo.StoreProduct("HuaWei?mate?40",?105)
          ????//?the?more?function?to?do,?the?more?code?is?repeated.
          ?default:
          ??repo?:=?productrepo.NewMockProductRepo()
          ??repo.StoreProduct("HuaWei?mate?40",?105)
          ????//?the?more?function?to?do,?the?more?code?is?repeated.
          ?}
          }


          在項(xiàng)目演進(jìn)過(guò)程中,我們不知道會(huì)迭代多少存儲(chǔ)庫(kù)對(duì)象,而通過(guò)ProductRepository接口,可以輕松地實(shí)現(xiàn)擴(kuò)展,而不必反復(fù)編寫相同邏輯的代碼。







          總結(jié)






          開(kāi)發(fā)中,我們常常提到要功能模塊化,本文的示例就是一個(gè)典型示例:通過(guò)接口為載體,一類服務(wù)就是一個(gè)接口,接口即服務(wù)。


          最后,你感受到Go接口賦予應(yīng)用的高擴(kuò)展性了嗎?



          推薦閱讀



          學(xué)習(xí)交流 Go 語(yǔ)言,掃碼回復(fù)「進(jìn)群」即可


          站長(zhǎng) polarisxu

          自己的原創(chuàng)文章

          不限于 Go 技術(shù)

          職場(chǎng)和創(chuàng)業(yè)經(jīng)驗(yàn)


          Go語(yǔ)言中文網(wǎng)

          每天為你

          分享 Go 知識(shí)

          Go愛(ài)好者值得關(guān)注


          瀏覽 44
          點(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>
                  国产高潮免费观看 | 日韩无码家庭乱伦 | 国产午夜精品福利 | 操碰网| 国产寡妇婬乱A毛片91精品 |