Go Interface 的優雅使用,讓程式碼更整潔更容易測試
在 Go 語言中,如果你還不會使用 Interface,那麼你還沒有真正掌握 Go 語言,Interface 是 Go 語言的精華和靈魂所在。接下來我們從一下幾個方面介紹 Go 語言裡面的 Interface。
Go Interface 是什麼?
簡單來說,Interface 是一組方法(Method)的集合,也是一種型別。比如如下是一個簡單的 Interface 定義。
type UserDataStore interface {
GetUserScore(ctx context.Context, id string) (int, error)
DeleteUser(ctx context.Context, id string) error
}
另外,在 Go 裡面是允許沒有任何方法的 Interface,對於這樣的空 Interface,可以認為任何的型別都實現了空 Interface。
假設,一個型別 A 實現了上述 Interface(UserDataStore)的方法,我們就可以認為 A 實現了上述 Interface,在實際的函式呼叫傳參中 A 是可以直接作為 UserDataStore 型別的引數。是的,可以理解為這就是我們常說的多型。
Go Interface 能做什麼?
那麼 Interface 除了可以實現多型,實際可以用來做什麼呢。以下是我認為比較重要的亮點。
1.依賴反轉,讓程式碼結構更整潔
我們來說一個比較常見的場景,一個 HTTP 介面,需要依賴資料庫來獲取使用者得分並返回給呼叫方。比較直接的寫法如下。
db := orm.NewDatabaseConnection() // 建立資料庫連結
res := db.Query("select score from user where user == 'xxx'") // 通過 SQL 語句查詢資料
return HTTP.Json(res) // 通過 Json 返回給呼叫方
這樣寫的壞處是,讓 HTTP 的介面依賴了具體資料庫底層的介面及實現,在資料庫查詢的功能沒有開發完成時,HTTP 介面是不能開始開發的。同時對於如果後續存在更換資料庫的可能,也不是很容易的擴充套件。比較推薦的寫法是下面這樣。
type UserDataStore interface {
GetUserScore(ctx context.Context, id string) (int, error)
DeleteUser(ctx context.Context, id string) error
}
// GetUserScoreHandler creates an HTTP handler that can get a user's score
func GetUserScoreHandler(userDataStore UserDataStore) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
id := req.Header.Get("x-user-id")
score, err := userDataStore.GetUserScore(req.Context(), id)
if err != nil {
fmt.Println("userDataStore.GetUserScore: ", err)
res.WriteHeader(500)
return
}
res.Write([]byte(fmt.Sprintf("%d", score)))
}
}
通過定義 Interface,將資料庫與 HTTP 介面進行解耦,HTTP 介面不再依賴實際的資料庫,程式碼可以單獨的編寫和編譯,程式碼依賴和結構更加的清晰了。資料具體的實現邏輯只需按 Interface 實現對應的介面就可以了,最終實現了依賴的整體的反轉。
2.提高程式的可測試性
回到剛才那個例子,如果我要對這個 HTTP 介面的邏輯做測試,我可以怎麼做?如果你沒有使用 Interface,那麼測試肯定要依賴一個實際的 DB,我想你會去新建一個測試庫,同時新建一些測試資料。
真的需要這樣麼?我們來一個比較好的實踐。通過 Interface,可以很容易的實現一個 Mock 版本的型別,通過替換邏輯可以很方便的實現測試資料的構造。
type mockUserDataStore struct {
pendingError error
pendingScore int
deletedUsers []string
}
func (m *mockUserDataStore) GetUserScore(ctx context.Context, id string) (int, error) {
return m.pendingScore, m.pendingError
}
func (m *mockUserDataStore) DeleteUser(ctx context.Context, id string) error {
if m.pendingError != nil {
return m.pendingError
}
m.deletedUsers = append(m.deletedUsers, id)
return nil
}
以上就可以很方便的去控制介面呼叫的時候,獲取使用者得分和刪除使用者的邏輯。實際的測試也就變得簡單了,也不用依賴真實的 DB,讓測試更加的可靠了。
func TestGetUserScoreHandlerReturnsScore(t *testing.T) {
req := httptest.NewRequest("GET", "/idk", nil)
res := httptest.NewRecorder()
userDataStore := &mockUserDataStore{
pendingScore: 3, // mock 資料
}
handler := GetUserScoreHandler(userDataStore) // 將 Mock 的方法傳遞到實際呼叫的地方,實現動態的替換
handler(res, req)
resultStr := string(res.Body.Bytes())
expected := fmt.Sprintf("%d", userDataStore.pendingScore)
if res.Code != 200 {
t.Errorf("Expected HTTP response 200 but got %d", res.Code)
}
if resultStr != expected {
t.Errorf("Expected body to contain value %q but got %q", expected, resultStr)
}
}
以上單元測試是不是就很簡單了。
如何優雅的使用 Go Interface?
以上的樣例其實都來自今天要推薦的開源專案。如果你非常關注架構和程式碼的整潔,以及程式碼的可測試性,非常推薦大家看一下。
更多專案詳情請檢視如下連結,尤其是專案中的程式碼,很簡單但非常值得看一下。
開源專案地址:https://github.com/Evertras/go-interface-examples
相關文章
- 使用Async,讓你的Node.js程式碼更優雅Node.js
- bgo: 讓構建 go 程式更容易Go
- 一些技巧讓你的 Laravel 程式碼更優雅Laravel
- 編寫更優雅的 JavaScript 程式碼JavaScript
- Spring Boot 整合 Lombok 讓程式碼更簡潔Spring BootLombok
- 使用 Macro 讓你的程式碼更簡潔,更具有可讀性Mac
- 帶引數的 Python 裝飾器讓你的程式碼更優雅Python
- 七種方法讓 Python 程式碼更容易維護Python
- 使用解構賦值與擴充套件運算子,讓你的程式碼更優雅賦值套件
- 經驗總結 | 重構讓你的程式碼更優美和簡潔
- 讓你的程式碼更優雅—去掉Xcode工程中某種型別的警告XCode型別
- Go 更強的程式碼潔癖,可以把 gofmt 給換了!Go
- PHPer這樣寫程式碼也許更優雅PHP
- 更優雅的在 mpvue 中使用 canvasVueCanvas
- Bartender 4: 讓你的 Mac 選單欄更整潔更高效Mac
- mp-redux:解耦小程式中的業務與檢視,讓測試更容易Redux解耦
- 幾個簡單的技巧讓你寫出的vue.js程式碼更優雅Vue.js
- 如何讓你的程式碼整潔漂亮
- 用 Node 抓站(二):Promise 使程式碼更優雅Promise
- 用Assert(斷言)封裝異常,讓程式碼更優雅(附專案原始碼)封裝原始碼
- 如何讓我們的模型更簡潔模型
- 讓你的DEVONthink UI 介面更簡潔?devUI
- 使用 Laravel Eloquent 構造器讓模型更簡潔Laravel模型
- 讓你的React表單操作更優雅(formik+yup)ReactORM
- 通過 Laravel 訊息通知使用 EasySms 簡訊服務,讓你的程式碼更簡潔Laravel
- 【譯】五個ES6功能,讓你編寫程式碼更容易
- Python 使用 backoff 更優雅的實現輪詢Python
- 更優雅的在 Xunit 中使用依賴注入依賴注入
- 使用go優雅地撰寫單元測試Go
- 【Android】在Kotlin中更優雅地使用LiveDataAndroidKotlinLiveData
- 讓程式碼變得優雅簡潔的神器:Java8 Stream流式程式設計Java程式設計
- .Net 7 的AOT的程式比託管程式碼更容易破解?
- 更優雅的使用 Parcel 進行前端專案的打包前端
- 教你更優雅地寫 API 之「列舉使用」API
- 通過facade(尤其是realtime facade)來使程式碼更優雅
- Java8的stream流讓操作集合更容易Java
- [譯]ES6提示和技巧,使您的程式碼更清晰,更短,更容易閱讀
- 如何寫出更優質的程式碼