<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 語言中,我為什么使用接口

          共 4091字,需瀏覽 9分鐘

           ·

          2020-09-14 19:38


          點擊上方藍色“Go語言中文網(wǎng)”關(guān)注我們,領(lǐng)全套Go資料,每天學(xué)習(xí)?Go?語言

          強調(diào)一下是我個人的見解以及接口在 Go 語言中的意義。

          如果您寫代碼已經(jīng)有了一段時間,我可能不需要過多解釋接口所帶來的好處,但是在深入探討 Go 語言中的接口前,我想花一兩分鐘先來簡單介紹一下接口。如果您對接口很熟悉,請先跳過下面這段。

          接口的簡單介紹

          在任一編程語言中,接口——方法或行為的集合,在功能和該功能的使用者之間構(gòu)建了一層薄薄的抽象層。在使用接口時,并不需要了解底層函數(shù)是如何實現(xiàn)的,因為接口隔離了各個部分(劃重點)。

          跟不使用接口相比,使用接口的最大好處就是可以使代碼變得簡潔。例如,您可以創(chuàng)建多個組件,通過接口讓它們以統(tǒng)一的方式交互,盡管這些組件的底層實現(xiàn)差異很大。這樣就可以在編譯甚至運行的時候動態(tài)替換這些組件。

          用 Go 的 io.Reader 接口舉個例子。io.Reader 接口的所有實現(xiàn)都有 Read(p []byte) (n int, err error) 函數(shù)。使用 io.Reader 接口的使用者不需要知道使用這個 Read 函數(shù)的時候那些字節(jié)從何而來。

          具體到 Go 語言

          在我使用 Go 語言的過程中,與我使用過的其他任何編程語言相比,我經(jīng)常發(fā)現(xiàn)其他的、不那么明顯的使用接口的原因。今天,我將介紹一個很普遍的,也是我遇到了很多次的使用接口的原因。

          Go 語言沒有構(gòu)造函數(shù)

          很多編程語言都有構(gòu)造函數(shù)。構(gòu)造函數(shù)是定義自定義類型(即 OO 語言中的類)時使用的一種建立對象的方法,它可以確保必須執(zhí)行的任何初始化邏輯均已執(zhí)行。

          例如,假設(shè)所有 widgets 都必須有一個不變的,系統(tǒng)分配的標(biāo)識符。在 Java 中,這很容易實現(xiàn):

          package?io.krancour.widget;

          import?java.util.UUID;

          public?class?Widget?{

          ????private?String?id;

          ????//?使用構(gòu)造函數(shù)初始化
          ????public?Widget()?{
          ????????id?=?UUID.randomUUID().toString();
          ????}

          ????public?String?getId()?{
          ????????return?id;
          ????}
          }
          class?App?{
          ????public?static?void?main(?String[]?args?){
          ????????Widget?w?=?new?Widget();
          ????????System.out.println(w.getId());
          ????}
          }

          從上面這個例子可以看到,沒有執(zhí)行初始化邏輯就無法實例化一個新的 Widget

          但是 Go 語言沒有此功能。:(

          在 Go 語言中,可以直接實例化一個自定義類型。

          定義一個 Widget 類型:

          package?widgets

          type?Widget?struct?{
          ????id?string
          }

          func?(w?Widget)?ID()?string?{
          ????return?w.id
          }

          可以像這樣實例化和使用一個 widget

          package?main

          import?(
          ????"fmt"
          ????"github.com/krancour/widgets"
          )

          func?main()?{
          ????w?:=?widgets.Widget{}
          ????fmt.Println(w.ID())
          }

          如果運行此示例,那么(也許)意料之中的結(jié)果是,打印出的 ID 是空字符串,因為它從未被初始化,而空字符串是字符串的“零值”。我們可以在 widgets 包中添加一個類似于構(gòu)造函數(shù)的函數(shù)來處理初始化:

          package?widgets

          import?uuid?"github.com/satori/go.uuid"

          type?Widget?struct?{
          ????id?string
          }

          func?NewWidget()?Widget?{
          ????return?Widget{
          ????????id:?uuid.NewV4().String(),
          ????}
          }

          func?(w?Widget)?ID()?string?{
          ????return?w.id
          }

          然后我們簡單地修改 main 來使用這個類似于構(gòu)造函數(shù)的新函數(shù):

          package?main

          import?(
          ????"fmt"
          ????"github.com/krancour/widgets"
          )

          func?main()?{
          ????w?:=?widgets.NewWidget()
          ????fmt.Println(w.ID())
          }

          執(zhí)行該程序,我們得到了想要的結(jié)果。

          但是仍然存在一個嚴(yán)重問題!我們的 widgets 包沒有強制用戶在初始一個 widget 的時候使用我們的構(gòu)造函數(shù)。

          變量私有化

          首先我們嘗試把自定義類型的變量私有化,以此來強制用戶使用我們規(guī)定的構(gòu)造函數(shù)來初始化 widget。在 Go 語言中,類型名、函數(shù)名的首字母是否大寫決定它們是否可被其他包訪問。名稱首字母大寫的可被訪問(也就是 public ),而名稱首字母小寫的不可被訪問(也就是 private )。所以我們把類型 Widget 改為類型 widget

          package?widgets

          import?uuid?"github.com/satori/go.uuid"

          type?widget?struct?{
          ????id?string
          }

          func?NewWidget()?widget?{
          ????return?widget{
          ????????id:?uuid.NewV4().String(),
          ????}
          }

          func?(w?widget)?ID()?string?{
          ????return?w.id
          }

          我們的 main 代碼保持不變,這次我們得到了一個 ID 。這比我們想要的要近了一步,但是我們在此過程中犯了一個不太明顯的錯誤。類似于構(gòu)造函數(shù)的 NewWidget 函數(shù)返回了一個私有的實例。盡管編譯器對此不會報錯,但這是一種不好的做法,下面是原因解釋。

          在 Go 語言中,**是復(fù)用的基本單位。其他語言中的**是復(fù)用的基本單位。如前所述,任何無法被外部訪問的內(nèi)容實質(zhì)上都是“包私有”,是該包的內(nèi)部實現(xiàn)細節(jié),對于使用這個包的使用者來說不重要。因此,Go 的文檔生成工具 godoc 不會為私有的函數(shù)、類型等生成文檔。

          當(dāng)一個公開的構(gòu)造函數(shù)返回一個私有的 widget 實例,實際上就陷入了一條死胡同。調(diào)用這個函數(shù)的人哪怕有這個實例,也絕對在文檔里找不到任何關(guān)于這個實例類型的描述,也更不知道 ID() 這個函數(shù)。Go 社區(qū)非常重視文檔,所以這樣做是不會被接受的。

          輪到接口上場了

          回顧一下,到目前為止,我們寫了一個類似于構(gòu)造函數(shù)的函數(shù)來解決 Go 語言缺乏構(gòu)造函數(shù)的問題,但是為了確保人們用該函數(shù)而不是直接實例化 Widget ,我們更改了該類型的可見性——將其重命名為 widget,即私有化了。雖然編譯器不會報錯,但是文檔中不會出現(xiàn)對這個私有類型的描述。不過,我們距離想要的目標(biāo)還近了一步。接下來就要使用接口來完成后續(xù)的了。

          通過創(chuàng)建一個**可被訪問的**、widget 類型可以實現(xiàn)的接口,我們的構(gòu)造函數(shù)可以返回一個公開的類型實例,并且會顯示在 godoc 文檔中。同時,這個接口的底層實現(xiàn)依然是私有的,使用者無法直接創(chuàng)建一個實例。

          package?widgets

          import?uuid?"github.com/satori/go.uuid"

          //?Widget?is?a?...
          type?Widget?interface?{
          ????//?ID?返回這個?widget?的唯一標(biāo)識符
          ????ID()?string
          }

          type?widget?struct?{
          ????id?string
          }

          //?NewWidget()?返回一個新的?Widget?實例
          func?NewWidget()?Widget?{
          ????return?widget{
          ????????id:?uuid.NewV4().String(),
          ????}
          }

          func?(w?widget)?ID()?string?{
          ????return?w.id
          }

          總結(jié)

          我希望我已經(jīng)充分地闡述了 Go 語言的這一特質(zhì)——構(gòu)造函數(shù)的缺失反而促進了接口的使用。

          在我的下一篇文章中,我將介紹一種幾乎與之相反的場景——在其他語言中要使用接口但是在 Go 語言中卻不必。


          via: https://medium.com/@kent.rancourt/go-pointers-why-i-use-interfaces-in-go-338ae0bdc9e4

          作者:Kent Rancourt[1]譯者:zhiyu-tracy-yang[2]校對:polaris1119[3]

          本文由 GCTT[4] 原創(chuàng)編譯,Go 中文網(wǎng)[5] 榮譽推出

          參考資料

          [1]

          Kent Rancourt: https://medium.com/@kent.rancourt

          [2]

          zhiyu-tracy-yang: https://github.com/zhiyu-tracy-yang

          [3]

          polaris1119: https://github.com/polaris1119

          [4]

          GCTT: https://github.com/studygolang/GCTT

          [5]

          Go 中文網(wǎng): https://studygolang.com/



          推薦閱讀


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


          站長 polarisxu

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

          不限于 Go 技術(shù)

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


          Go語言中文網(wǎng)

          每天為你

          分享 Go 知識

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


          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  少妇AV电影了 | 人妻体内射精一区二区三区 | 成人黄色一区二区 | 精品无人国产偷自产在线 | 天天干天天操天天摸 |