Go語言開發過程中,什麼時候使用方法,什麼時候使用interface進行方法封裝呢?

剛開始做Go語言開發的過程中,經常會有一個疑問,使用接口封裝的方法和不使用接口封裝的方法,在本質上是沒有區別的,但是在讀了io標準庫之後,對兩者的使用有了進一步的理解,在這裏總結一下。

1.什麼是方法

在 go語言中可以通過下面的形式定義方法

func (A type) function_name(args) {}

其中A爲一種定義好的類型,args爲傳入的參數,調用方法時,必須使用type.function_name的形式調用,type是定義好的類型,舉個例子:

package main

import (
	"fmt"
)
// 定義結構體類型User
type User struct {
	Name string
	Age  int
}
// User類型的方法,用來輸出用戶名和密碼
func (u *User) GetUserInfo() {
	fmt.Println("Name:", u.Name, "Age:", u.Age)
	return
}

func main() {
	var u User
	u.Name = "random"
	u.Age = 18
	// 調用方法
	u.GetUserInfo()
}

Output:

$ go run main.go
Name: random Age: 18

2.如何使用接口封裝方法

Go語言通過接口實現了鴨子類型(duck-typing):“當看到一直鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱爲鴨子”。我們並不關心對象是什麼類型,到底是不是鴨子,只關心行爲,這也就是其他編程語言中的多態。
下面舉一個例子:

package main

import (
	"fmt"
)
// 對方法進行封裝
type IUser interface {
	GetUserInfo()
}

// 定義結構體類型User
type User struct {
	Name string
	Age  int
}
// User類型的方法,用來輸出用戶名和密碼
func (u *User) GetUserInfo() {
	fmt.Println("Name:", u.Name, "Age:", u.Age)
	return
}

func main() {
	var u User
	u.Name = "random"
	u.Age = 18
	var user IUser = &u
	user.GetUserInfo()
}

Output:

$ go run main.go
Name: random Age: 18

與方法相比這種調用方式顯得複雜,那麼什麼時候使用接口封裝方法比較好呢?

3.什麼時候使用接口

這裏以io標準庫爲例,在io包中最重要的是兩個接口:Reader和Writer接口,首先來介紹這兩個接口。

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

Reader 接口包裝了基本的 Read 方法。

Read 將 len( p) 個字節讀取到 p 中。它返回讀取的字節數 n(0 <= n <= len§)以及任何遇到的錯誤。即使 Read 返回的 n < len§,它也會在調用過程中使用 p的全部作爲暫存空間。若一些數據可用但不到 len( p) 個字節,Read 會照例返回可用的東西,而不是等待更多。

當 Read 在成功讀取 n > 0 個字節後遇到一個錯誤或 EOF 情況,它就會返回讀取的字節數。它會從相同的調用中返回(非nil的)錯誤或從隨後的調用中返回錯誤(和 n == 0)。這種一般情況的一個例子就是 Reader 在輸入流結束時會返回一個非零的字節數,可能的返回不是 err == EOF 就是 err == nil。無論如何,下一個 Read 都應當返回 0, EOF。

調用者應當總在考慮到錯誤 err 前處理 n > 0 的字節。這樣做可以在讀取一些字節,以及允許的 EOF 行爲後正確地處理I/O錯誤。
Read 的實現會阻止返回零字節的計數和一個 nil 錯誤,調用者應將這種情況視作空操作。

io庫中有一個函數: func ReadFull(r Reader, buf []byte) (n int, err error),這個函數可以把對象 r 中的數據讀出來,然後存入一個緩衝區 buf 中,以便其它代碼可以處理 buf 中的數據,ReadFull 可以讀取任何對象的數據,但是有個前提,就是這個對象必須符合 Reader 的標準
讀到這裏大家應該能看出接口封裝的作用了,它可以實現在不知道對象類型的情況下可以對對象進行一些操作。

// 定義一個 str 類型(以 string 爲基類型)
type str string

// 實現 str 類型的 Read 方法
func (s str) Read(p []byte) (n int, err error) {
	i, ls, lp := 0, len(s), len(p)
	for ; i < ls && i < lp; i++ {
		// 將小寫字母轉換爲大寫字母,然後寫入 p 中
		if s[i] >= 'a' && s[i] <= 'z' {
			p[i] = s[i] + 'A' - 'a'
		} else {
			p[i] = s[i]
		}
	}
	// 根據讀取的字節數設置返回值
	switch i {
	case lp:
		return i, nil
	case ls:
		return i, io.EOF
	default:
		return i, errors.New("read fail")
	}
}
func main() {
	s := str("Hello World!")      // 創建 str 對象 us
	buf := make([]byte, 32)       // 創建緩衝區 buf
	n, err := io.ReadFull(s, buf) // 將 s 中的數據讀取到 buf 中
	if err != nil && err != io.ErrUnexpectedEOF {
		return
	}
	fmt.Printf("%s\nlength:%d", buf, n) // 顯示 buf 中的內容
}

Output:

$ go run main.go
HELLO WORLD!
length:12
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章