一、聲明接口
1 type Result interface { 2 LastInsertId() (int64, error) 3 RowsAffected() (int64, error) 4 }
二、實現接口,這里卻將接口作為成員變量,進而將接口的實現轉換為接口的調用,僅僅是封裝了接口,實際上並沒有真的實現,而是坐等別人去實現
1 // 一把鎖 2 // 一個結果集的假接口實現,表示需要的功能,讓他人來具體實現。假裝實現了某個接口,其實是調用了內部接口的對應方法
3 type driverResult struct { 4 sync.Locker 5 resi driver.Result 6 } 7 // Result 是對已執行 SQL 命令的總結,。 8 // LastInsertId() 會返回一個由數據庫生成的整數, 這個整數是對命令的響應。 在插入一個新的數據行時, 這個整數通常來源於數據表中的自增數據列。 9 // 並不是所有數據庫都支持這個特性, 並且各個數據庫在實現這個特性時使用的語句也會有所不同。 10 // RowsAffected() 返回受到更新、插入或者刪除操作影響的行數量, 並不是所有數據庫或者所有數據庫驅動都支持這個特性。
11 type Result interface { 12 LastInsertId() (int64, error) 13 RowsAffected() (int64, error) 14 } 15
16 func (dr driverResult) LastInsertId() (int64, error) { 17 dr.Lock() 18 defer dr.Unlock() 19 return dr.resi.LastInsertId() 20 } 21
22 func (dr driverResult) RowsAffected() (int64, error) { 23 dr.Lock() 24 defer dr.Unlock() 25 return dr.resi.RowsAffected() 26 }
Go 中的 interface 是一種類型,更准確的說是一種抽象類型 abstract type,一個 interface 就是包含了一系列行為的 method 集合,interface 的定義很簡單:
1 package io 2 3 type Writer interface { 4 Write(p []byte) (n int, err error) 5 }
如果一個 concrete type 實現了某個 interface,我們說這個 concrete type 實現了 interface 包含的所有 method,必須是所有的 method。Go 中的 interface 不同於其它語言,它是隱式的 implicitly,這意味着對於一個已有類型,你可以不用更改任何代碼就可以讓其滿足某個 interface。
在 Go 的標准庫 fmt
中有一系列的方法可以很好的詮釋 interface 是如何應用到實踐當中的。
1 package fmt 2 3 func Fprintf(w io.Writer, format string, args ...interface{}) (int, error) 4 5 func Printf(format string, args ...interface{}) (int, error) { 6 return Fprintf(os.Stdout, format, args...) 7 } 8 9 func Sprintf(format string, args ...interface{}) string { 10 var buf bytes.Buffer 11 Fprintf(&buf, format, args...) 12 return buf.String() 13 }
在
中的前綴 Printf
函數中,調用 Fprintf
時指定的輸出是標准輸出,這正是 Printf
的功能:Printf formats according to a format specifier and writes to standard output,根據指定的格式化要求輸出到標准輸出,os.Stdout
的類型是 *os.File
。FprintfF
表示 File
,意思是格式化的輸出被輸出到函數指定的第一個 File
類型的參數中。
同樣在 Sprintf
函數中,調用 Fprintf
時指定的輸出是一個指向某個 memory buffer 的指針,其類似一個 *os.File
。
雖然 bytes.Buffer
和 os.Stdout
是不同的,但是它們都可以被用於調用同一個函數 Fprintf
,就是因為 Fprintf
的第一個參數是接口類型 io.Writer
,而 bytes.Buffer
和 os.Stdout
都實現了這個 interface,即它們都實現了 Write
這個 method,這個 interface 並不是一個 File
卻完成了類似 File
的功能。
Fprintf
其實並不關心它的第一個參數是一個 file 還是一段 memory,它只是調用了 Write
method。這正是 interface 所關注的,只在乎行為,不在乎其值,這種能力讓我們可以非常自由的向 Fprintf
傳遞任何滿足 io.Writer
的 concrete type,這是 Go interface 帶來的 substitutability
可替代性,object-oriented programming 的一種重要特性。
看個例子:
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 type ByteCounter int 8 9 10 func (c *ByteCounter) Write(p []byte) (int, error) { 11 *c += ByteCounter(len(p)) // convert int to ByteCounter 12 return len(p), nil 13 } 14 15 func main() { 16 17 var c ByteCounter 18 c.Write([]byte("hello")) 19 fmt.Println(c) // 5 #1 20 fmt.Fprintf(&c, "hello") #2 21 fmt.Println(c) // 10 #3 22 }
實現了
這就是 Go 中的 interface 所具有的最基本的功能:作為一種 abstract type,實現各種 concrete type 的行為統一。ByteCounterWrite
method,它滿足 io.Writer
interface,Write
method 計算傳給它的 byte slice 的長度並且賦值給自身,所以 #1
輸出到標准輸出的是它的值 5,正如前文所言,調用 fmt.Fprintf
時再次調用了 c 的 Write
method,所以 #3
輸出是 10。