## 5.11\. 接口和其他類型
### 5.11.1\. 接口
Go中的接口提供了一類對象的抽象。我們在前面已經看到了關于接口的一些例子。 我們可以給新定義的對象實現一個String方法,這樣就可以用 Fprintf輸出該類型的值。同樣,Fprintf可以將 結果輸出到任意實現了Write方法的對象。接口一般只包含一類方法, 并且以ed后綴的方式命名,例如io.Writer接口對應Write 方法實現。
一種類型可以實現多個接口。例如,如果想支持sort包中的排序 方法,那么只需要實現sort.Interface接口的Len()、 Less()、Swap(i, j int)方法就可以了。下面的例子 實現了sort.Interface接口的同時,還定制了輸出函數。
```
type Sequence []int
// Methods required by sort.Interface.
func (s Sequence) Len() int {
return len(s)
}
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Method for printing - sorts the elements before printing.
func (s Sequence) String() string {
sort.Sort(s)
str := "["
for i, elem := range s {
if i > 0 {
str += " "
}
str += fmt.Sprint(elem)
}
return str + "]"
}
```
### 5.11.2\. 轉換
Sequence 的 String 方法重做了 Sprint 在切片的工作。我們可以在調用 Sprint 前把 Sequence 轉為普通的 []int。
```
func (s Sequence) String() string {
sort.Sort(s)
return fmt.Sprint([]int(s))
}
```
轉換使得 s 被當作普通的切片,因此得到默認的排版。不加轉換,Sprint 會發現 Sequence 的 String 方法,進而無窮遞歸。如果忽略類型名稱,Sequence 和 []int 的類型相同, 它們之間的轉換是合法的。轉換不會得到新值,它只是暫時假裝現有值是新類型。(其它合法的轉換,如從整型到浮點型,會生成新值。)
地道的 Go 程序會轉換表達式的類型來使用一組不同的方法。例如,我們可以用現有類型 sort.IntArray 把整個例子縮減為:
```
type Sequence []int
// Method for printing - sorts the elements before printing
func (s Sequence) String() string {
sort.IntArray(s).Sort()
return fmt.Sprint([]int(s))
}
```
現在,無需讓 Sequence 實現多個界面(排序和打印),我們使用了把數據轉換為多種類型(Sequence,sort.IntArray 和 []int)的能力,每個來完成一部分的工作。這實際上不常見但很有效。
### 5.11.3\. Generality(泛化)
如果某類型的存在只為了實現某界面,而除此之外沒有其它導出的方法,則此類型也不需導出。只導出界面明確了只有行為有價值,而不是實現,其它的不同特性的實現可以鏡像其原來的類型。這樣也避免了為同一方法的不同實現做文檔。
此時,架構函數應返回界面而不是實現類型。例如,哈希庫的crc32.NewIEEE() 和 adler32.New() 都返回界面類型 hash.Hash32。 替換一個 Go 出現的 CRC-32 算法為 Adler-32 只需改變架構函數的調用;其余的代碼不受算法改變的影響。
同樣的方式使得 crypto/block 包的流密(streaming cipher)算法與鏈接在一起的塊密(block cipher)相區隔。比對 bu?o 包,它們包裝了界面,返回 hash.Hash,io.Reader 和 io.Writer 界面值,而不是特定的實現。
crypto/block 界面包括:
```
type Cipher interface {
BlockSize() int
Encrypt(src, dst []byte)
Decrypt(src, dst []byte)
}
// NewECBDecrypter returns a reader that reads data
// from r and decrypts it using c in electronic codebook (ECB) mode.
func NewECBDecrypter(c Cipher, r io.Reader) io.Reader
// NewCBCDecrypter returns a reader that reads data
// from r and decrypts it using c in cipher block chaining (CBC) mode
// with the initialization vector iv.
func NewCBCDecrypter(c Cipher, iv []byte, r io.Reader) io.Reader
```
NewECBDecrypter 和 NewCBCReader 不只用于某特定的加密算法和數據源,而是任意的 Cipher 界面的實現和任意的 io.Reader。因為它們返回 io.Reader 界面值,替換 ECB 加密為 CBC 加密只是局部修改。 架構函數必須編輯,但因為周圍代碼必須只把結果作為io.Reader,它不會注意到有什么不同。
### 5.11.4\. 接口和方法
因為幾乎任何東西都可加以方法,幾乎任何東西都可滿足某界面。一個展示的例子是 http 包定義的 Handler 界面。任何物件實現了Handler 都可服務 HTTP 請求。
```
type Handler interface {
ServeHTTP(*Conn, *Request)
}
```
ResponseWriter 本身是個界面,它提供一些可訪問的方法來返回客戶的請求。這些方法包括標準的 Write 方法。因此 http.ResponseWriter 可用在 io.Writer 可以使用的地方。Request 是個結構,包含客戶請求的一個解析過的表示。
為求簡短,我們忽略 POST 并假定所有 HTTP 請求都是 GET;此簡化不會影響經手者的設置。下面一個小而全的經手者實現了網頁訪問次數的計數。
```
// Simple counter server.
type Counter struct {
n int
}
func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) {
ctr.n++
fmt.Fprintf(c, "counter = %d\n", ctr.n)
}
```
(注意 Fprintf 怎樣打印到 http.ResponseWriter)。作為參考,這里是怎樣把服務者加在一個 URL 樹的節點上。
```
import "http"
...
ctr := new(Counter)
http.Handle("/counter", ctr)
```
可是為何把 Counter 作為結構呢?一個整數能夠了。(接受者需是指針,使增量帶回調用者)。
```
// Simpler counter server.
type Counter int
func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) {
*ctr++
fmt.Fprintf(c, "counter = %d\n", *ctr)
}
```
當某頁被訪問時怎樣通知你的程序更新某些內部狀態呢?給網頁貼個信道。
```
// A channel that sends a notification on each visit.
// (Probably want the channel to be buffered.)
type Chan chan *http.Request
func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) {
ch <- req
fmt.Fprint(c, "notification sent")
}
```
最后,讓我們在 /args 顯示啟動服務器時的參量。寫個打印參量的函數很容易:
```
func ArgServer() {
for i, s := range os.Args {
fmt.Println(s)
}
}
```
怎樣把它變成 HTTP 服務器呢?我們可以把 ArgServer 作為某個類型的方法再忽略其值,也有更干凈的做法。既然我們可以給任意非指針和界面的類型定義方法,我們可以給函數寫個方法。http 包里有如下代碼:
```
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler object that calls f.
type HandlerFunc func(*Conn, *Request)
// ServeHTTP calls f(c, req).
func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) {
f(c, req)
}
```
HandlerFunc 是個帶 ServeHTTP 方法的類型, 所以此類的值都可以服務 HTTP 請求。我們來看看此方法的實現:接受者是個函數,f,方法調用 f 。看起來很怪,但和,比如,接受者是信道,而方法發送到 此信道,沒什么不同。
要把 ArgServer 變為 HTTP 服務器, 我們首先改成正確的簽名:
```
// Argument server.
func ArgServer(c *http.Conn, req *http.Request) {
for i, s := range os.Args {
fmt.Fprintln(c, s)
}
}
```
ArgServer 現在和 HandlerFunc 有同樣的簽名,就可以轉成此類使用其方法,就像我們把 Sequence 轉為 IntArray 來使用 IntArray.Sort 一樣。設置代碼很簡短:
```
http.Handle("/args", http.HandlerFunc(ArgServer))
```
當有人訪問 /args 頁時,此頁的經手者有值 ArgServer 和類型HandlerFunc。HTTP 服務器啟動此類型的 ServeHTTP 方法,用ArgServer 作為接受者,反過來調用 ArgServer (通過啟動handlerFunc.ServeHTTP 的 f(w, req) 。)參量被顯示出來。
此節中我們從一個結構,整數,信道和一個函數制造出一個 HTTP 服務器,全賴于界面就是一套方法,可定義在(幾乎)任何類型上。
- 1. 關于本文
- 2. Go語言簡介
- 3. 安裝go環境
- 3.1. 簡介
- 3.2. 安裝C語言工具
- 3.3. 安裝Mercurial
- 3.4. 獲取代碼
- 3.5. 安裝Go
- 3.6. 編寫程序
- 3.7. 進一步學習
- 3.8. 更新go到新版本
- 3.9. 社區資源
- 3.10. 環境變量
- 4. Go語言入門
- 4.1. 簡介
- 4.2. Hello,世界
- 4.3. 分號(Semicolons)
- 4.4. 編譯
- 4.5. Echo
- 4.6. 類型簡介
- 4.7. 申請內存
- 4.8. 常量
- 4.9. I/O包
- 4.10. Rotting cats
- 4.11. Sorting
- 4.12. 打印輸出
- 4.13. 生成素數
- 4.14. Multiplexing
- 5. Effective Go
- 5.1. 簡介
- 5.2. 格式化
- 5.3. 注釋
- 5.4. 命名
- 5.5. 分號
- 5.6. 控制流
- 5.7. 函數
- 5.8. 數據
- 5.9. 初始化
- 5.10. 方法
- 5.11. 接口和其他類型
- 5.12. 內置
- 5.13. 并發
- 5.14. 錯誤處理
- 5.15. Web服務器
- 6. 如何編寫Go程序
- 6.1. 簡介
- 6.2. 社區資源
- 6.3. 新建一個包
- 6.4. 測試
- 6.5. 一個帶測試的演示包
- 7. Codelab: 編寫Web程序
- 7.1. 簡介
- 7.2. 開始
- 7.3. 數據結構
- 7.4. 使用http包
- 7.5. 基于http提供wiki頁面
- 7.6. 編輯頁面
- 7.7. template包
- 7.8. 處理不存在的頁面
- 7.9. 儲存頁面
- 7.10. 錯誤處理
- 7.11. 模板緩存
- 7.12. 驗證
- 7.13. 函數文本和閉包
- 7.14. 試試!
- 7.15. 其他任務
- 8. 針對C++程序員指南
- 8.1. 概念差異
- 8.2. 語法
- 8.3. 常量
- 8.4. Slices(切片)
- 8.5. 構造值對象
- 8.6. Interfaces(接口)
- 8.7. Goroutines
- 8.8. Channels(管道)
- 9. 內存模型
- 9.1. 簡介
- 9.2. Happens Before
- 9.3. 同步(Synchronization)
- 9.4. 錯誤的同步方式
- 10. 附錄
- 10.1. 命令行工具
- 10.2. 視頻和講座
- 10.3. Release History
- 10.4. Go Roadmap
- 10.5. 相關資源