亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
慕課專欄

目錄

索引目錄

改善Go語言編程質量的50個有效實踐

原價 ¥ 78.00

立即訂閱
03 Go 語言的設計哲學之二:組合
更新時間:2020-09-08 09:34:57
沒有引發任何行動的思想都不是思想,而是夢想。 —— 馬丁

第二條原則: 偏好組合,正交解耦

當我們有必要采用另外一種方式處理數據時,我們應該有一些耦合程序的方式,就像花園里將澆水的軟管通過預置的螺絲扣擰入另一段那樣,這也是 Unix IO 采用的方式。- 道格·麥克羅伊,Unix 管道的發明者(1964)

C++、Java 等主流面向對象(以下簡稱 OO)語言通過龐大的、自上而下的類型體系、繼承、顯式接口實現等機制將程序的各個部分耦合起來,但在 Go 語言中我們找不到經典 OO 的語法元素、類型體系和繼承機制,或者說 Go 語言本質上就不屬于經典 OO 語言范疇。針對這種情況,很多人會問:那 Go 語言是如何將程序的各個部分有機地耦合在一起的呢?就像上面引述的道格.麥克羅伊的那句話中的澆水軟管那樣,Go 語言遵從的設計哲學也是組合。

在詮釋組合之前,我們可以先來了解一下 Go 在語法元素設計時是如何為組合哲學的應用奠定基礎的。

在 Go 語言設計層面,Go 設計者為 gopher 們提供了正交的語法元素供后續組合使用,包括:

  • Go 語言無類型體系(type hierarchy),類型之間是獨立的,沒有子類型的概念;
  • 每個類型都可以有自己的方法集合,類型定義與方法實現是正交獨立的;
  • 接口(interface)與其實現之間"隱式關聯";
  • 包(package)之間是相對獨立的,沒有子包的概念。

我們看到無論是包、接口還是一個個具體的類型定義(包括類型的方法集合),Go 語言為我們呈現了這樣的一幅圖景:一座座沒有關聯的“孤島”,但每個島內又都很精彩?,F在擺在面前的工作就是在這些孤島之間以最適當的方式建立關聯(耦合),形成一個"整體"。Go 采用了組合的方式,也是唯一的方式。

Go 語言提供了的最為直觀的組合的語法元素就是type embedding,即類型嵌入。通過類型嵌入,我們可以將已經實現的功能嵌入到新類型中,以快速滿足新類型的功能需求,這種方式有些類似經典 OO 的“繼承”,但在原理上與經典 OO 的繼承完全不同。這是一種 Go 精心設計的“語法糖”,被嵌入的類型和新類型兩者之間沒有任何關系,甚至相互完全不知道對方的存在,更沒有經典 OO 那種父類、子類的關系以及向上、向下轉型(type casting)。通過新類型實例調用方法時,方法的匹配取決于方法名字,而不是類型。這種組合方式,我稱之為“垂直組合”,即通過類型嵌入,快速讓一個新類型“復用”其他類型已經實現的能力,實現功能的垂直擴展。

下面是一個類型嵌入的例子:

  // Go標準庫:sync/pool.go
  type poolLocal struct {
      private interface{}   // Can be used only by the respective P.
      shared  []interface{} // Can be used by any P.
      Mutex                 // Protects shared.
      pad     [128]byte     // Prevents false sharing.
  }

我們在 poolLocal 這個 struct 中嵌入類型 Mutex,被嵌入的 Mutex 類型的方法集合會被提升到外面的類型中。比如,這里的 poolLocal 將擁有 Mutex 類型的 Lock 和 Unlock 方法。實際調用時,方法調用實際會被傳給 poolLocal 中的 Mutex 實例。

我們在標準庫中還經??吹筋愃迫缦碌?interface 類型嵌入的代碼:

  type ReadWriter interface {
      Reader
      Writer
  }

通過在 interface 中嵌入 interface type,實現接口行為的聚合,組成大接口,這種方式在標準庫中尤為常用,并且已經成為了 Go 語言的一種常見的慣用法。

interface 是 Go 語言中真正的魔法,是 Go 語言的一個創新設計,它只是方法集合,并且它與實現者之間的關系是隱式的,它讓程序內部各部分之間的耦合降至最低,同時它也是連接程序各個部分之間“紐帶”。隱式的 interface 實現會不經意間滿足:依賴抽象、里氏替換、接口隔離等原則,這在其他語言中是需要很"刻意"的設計謀劃才能實現的,但在 Go interface 來看,一切卻是自然而然的。

通過 interface 將程序內部各個部分組合在一起的方法,我這里稱之為水平組合。水平組合的“模式”很多,比如:一種常見方法就是:通過接受 interface 類型參數的普通函數進行組合,例如下面代碼。

  func ReadAll(r io.Reader) ([]byte, error)
  func Copy(dst Writer, src Reader) (written int64, err error)

ReadAll 通過 io.Reader 這個接口將 io.Reader 的實現與 ReadAll 所在的包低耦合的水平組合在一起了。類似的水平組合“模式”還有 wrapper、middleware 等,這里就不展開了,在后面講到 interface 時再詳細敘述。

此外,Go 語言內置的并發能力也可以通過組合的方式實現“對計算能力的串聯”,比如:通過 goroutine+channel 的組合實現類似 Unix Pipe 的能力。

綜上,組合原則的應用塑造了 Go 程序的骨架結構。類型嵌入為類型提供的垂直擴展能力,interface 是水平組合的關鍵,它好比程序肌體上的“關節”,給予連接“關節”的兩個部分各自“自由活動”的能力,而整體上又實現了某種功能。組合也讓遵循“簡單”原則的 Go 語言的表現力絲毫不遜色于其他復雜的主流編程語言。

}
立即訂閱 ¥ 78.00

你正在閱讀課程試讀內容,訂閱后解鎖課程全部內容

千學不如一看,千看不如一練

手機
閱讀

掃一掃 手機閱讀

改善Go語言編程質量的50個有效實踐
立即訂閱 ¥ 78.00

舉報

0/150
提交
取消