Go從入門到精通——介面(interface)——理解型別與介面的關係
理解型別與介面的關係
型別和介面之間有一對多和多對一的關係,下面將列舉出這些常見的概念,以方便理解介面與型別在複雜環境下的實現關係。
一、一個型別可以實現多個介面
一個型別可以同時實現多個介面,而介面間彼此獨立,不知道對方的實現。
網路上的兩個程式通過一個雙向的通訊連線實現資料的交換,連線的一端稱為一個 Socket。Socket 能夠同時讀取和寫入資料,這個特性與檔案類似。因此,開發中把檔案和 Socket 都具備的讀寫特性抽象為獨立的讀寫器概念。
把 Socket 能夠寫入資料和需要關閉的特性使用介面來描述,程式碼如下:
type Socket struct{ } func (s *Scoket) Write(p []byte)(n int, err error){ return 0,nil } func (s *Socket) Close() error{ return nil }
Socket 結構的 Write() 方法實現了 io.Writer 介面:
type Writer interface{
Write(p []byte)(n int, err error)
}
同時,Socket 結構也實現了 io.Close 介面:
type Closer interface{
Close() error
}
使用 Socket 實現的 Writer 介面的程式碼,無須瞭解 Writer 介面的實現者是否具備 Closer 介面的特性。同樣,使用 Close 介面的程式碼也不知道 Socket 已經實現了 Writer 介面。如下圖:
在程式碼中使用 Socket 結構實現的 Writer 介面和 Closer 介面程式碼如下:
package main //使用 io.Writer 的程式碼,並不知道 Socket 和 io.Closer 的存在 func usingWriter( writer io.Writer){ wirter.Write(nil) } //使用 io.Close,並不知道 Socket 和 io.Writer 的存在 func usingCloser( closter io.Closer){ closter.Close() } func main(){ //例項化 Socket s := new(Socket) usingWriter(s) usingCloser(s) }
usingWriter() 和 usingCloser() 完全獨立,互相不知道對方的存在,也不知道自己使用的介面是 Scoket 實現的。
二、多個型別可以實現相同的介面
一個介面的方法,不一定需要一個型別完全實現,介面的方法可以通過在型別中嵌入其他型別或者結構體來實現。也就是說,使用者並不關心某個介面的方法是通過一個型別完全實現的,還是通過多個結構嵌入到一個結構體中拼湊起來共同實現的。
Service 介面定義了兩個方法:一個是開啟服務的方法(Start()),一個是輸出日誌的方法(Log())。使用 GameService 結構來實現 Service,GameService 自己的結構只能實現 Start() 方法,而 Service 介面中 Log() 方法已經被一個能輸出日誌的日誌器(Logger)實現了,無須再進行 GameService 封裝,或者重新實現一遍。所以,選擇將 Logger 嵌入到 GameService 能最大程度地避免程式碼冗餘,簡化程式碼結構。程式碼實現如下:
package main
type Service interface {
Start()
Log(string)
}
//日誌器
type Logger struct {
}
//實現 Service 的 Log() 方法
func (l *Logger) Log(s string) {
}
//遊戲服務
type GameService struct {
Logger
}
//實現 Service 的 Start() 方法
func (g *GameService) Start() {
}
func main() {
var s Service = new(GameService)
s.Start()
s.Log("hello")
}
例項化 GameService,並將例項賦給 Service,s 就可以使用 Start()方法和 Log() 方法,其中,Start() 由 GameService 實現,Log() 方法由 Logger 實現。