Golang核心編程(6)-面向對象之接口


更多關於Golang核心編程知識的文章請看:Golang核心編程(0)-目錄頁


一、其他語言的侵入式接口

Go語言的接口並不是其他語言(C++、Java、C#等)中所提供的接口概念。
在Go語言出現之前,接口主要作爲不同組件之間的契約存在。對契約的實現是強制的,你必須聲明你的確實現了該接口。爲了實現一個接口,你需要從該接口繼承:

interface IFoo {
void Bar();
}
class Foo implements IFoo { // Java文法
// ...
}
class Foo : public IFoo { // C++文法
// ...
}
IFoo* foo = new Foo;

即使另外有一個接口 IFoo2 實現了與 IFoo 完全一樣的接口方法甚至名字也叫 IFoo 只不過位於不同的名字空間下,編譯器也會認爲上面的類 Foo 只實現了 IFoo 而沒有實現 IFoo2 接口。

這類接口我們稱爲侵入式接口。“侵入式”的主要表現在於實現類需要明確聲明自己實現了某個接口。這種強制性的接口繼承是面向對象編程思想發展過程中一個遭受相當多置疑的特性。

二、 Golang的非侵入式接口

在Go語言中,一個類只需要實現了接口要求的所有函數,我們就說這個類實現了該接口,例如:

type File struct {
// ...
}
func (f *File) Read(buf []byte) (n int, err error)
func (f *File) Write(buf []byte) (n int, err error)
func (f *File) Seek(off int64, whence int) (pos int64, err error)
func (f *File) Close() error

這裏我們定義了一個 File 類,並實現有 Read() 、 Write() 、 Seek() 、 Close() 等方法。設想我們有如下接口:

type IFile interface {
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
Seek(off int64, whence int) (pos int64, err error)
Close() error
}

type IReader interface {
Read(buf []byte) (n int, err error)
}

type IWriter interface {
Write(buf []byte) (n int, err error)
}

type ICloser interface {
Close() error
}

儘管 File 類並沒有從這些接口繼承,甚至可以不知道這些接口的存在,但是 File 類實現了
這些接口,可以進行賦值:

var file1 IFile = new(File)
var file2 IReader = new(File)
var file3 IWriter = new(File)
var file4 ICloser = new(File)

Go的非侵入式接口有許多好處:

其一,在Go中,類的繼承樹並無意義,你只需要知道這個類實現了哪些方法,每個方法是啥含義就足夠了。
其二,實現類的時候,只需要關心自己應該提供哪些方法,不用再糾結接口需要拆得多細才
合理。接口由使用方按需定義,而不用事前規劃。
其三,不用爲了實現一個接口而導入一個包,因爲多引用一個外部的包,就意味着更多的耦
合。接口由使用方按自身需求來定義,使用方無需關心是否有其他模塊定義過類似的接口。

三、 接口判斷

接口查詢語法,代碼如下:

var file1 Writer = ...
if file5, ok := file1.(two.IStream); ok {
...
}

這個 if 語句檢查 file1 接口指向的對象實例是否實現了 two.IStream 接口,如果實現了,則執行特定的代碼。

接口查詢是否成功,要在運行期才能夠確定。它不像接口賦值,編譯器只需要通過靜態類型檢查即可判斷賦值是否可行。

四、類型查詢

在Go語言中,還可以更加直截了當地詢問接口指向的對象實例的類型,例如:

	var v1 interface{} = ...
	switch v := v1.(type) {
	case int: // 現在v的類型是int
	case string: // 現在v的類型是string
	...
}

五、接口組合

Go語言同樣支持接口組合。我們已經介紹過Go語言包中 io.Reader接口和 io.Writer 接口,接下來我們再介紹同樣來自於 io 包的另一個接口 io.ReadWriter :

	// ReadWriter接口將基本的Read和Write方法組合起來
	type ReadWriter interface {
	Reader
	Writer
	}

這個接口組合了 Reader 和 Writer 兩個接口,它完全等同於如下寫法:

	type ReadWriter interface {
	Read(p []byte) (n int, err error)
	Write(p []byte) (n int, err error)
	}

因爲這兩種寫法的表意完全相同: ReadWriter 接口既能做 Reader 接口的所有事情,又能做
Writer 接口的所有事情。在Go語言包中,還有衆多類似的組合接口,比如 ReadWriteCloser 、ReadWriteSeeker 、 ReadSeeker 和 WriteCloser 等。
可以認爲接口組合是類型匿名組合的一個特定場景,只不過接口只包含方法,而不包含任何成員變量。

六、Any 類型

由於Go語言中任何對象實例都滿足空接口 interface{} ,所以 interface{} 看起來像是可
以指向任何對象的 Any 類型,如下:

var v1 interface{} = 1 // 將int類型賦值給interface{}
var v2 interface{} = "abc" // 將string類型賦值給interface{}
var v3 interface{} = &v2 // 將*interface{}類型賦值給interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章