我正在学习Golang并尝试学习一些好的实践。我看了这个视频:https://www.youtube.com/watch?v=EqniGcAijDI,在 8:45 左右,他开始实现数据存储层的接口。
我理解接口允许业务层和技术层解耦,这将使更改数据库引擎等变得更容易。
我不明白的是我应该在该接口上实现哪个方法?
在视频中他添加了一个非常具体的方法:
Get(int) *types.User
因此,每个实现该接口的存储都必须有一个 Get 方法,该方法返回指向用户的指针。
那么,我应该在我的存储接口中添加我数据库所有对象的所有方法吗?
我错过了什么吗?
谢谢!
是的,你知道。它不是关于应该实现接口的模型,而是关于数据访问层。
他展示的示例相当简单:
type Storage interface {
Get(int) *types.User
}
假设你这样使用它:
// GetUser returns a http.Handler which uses a Storage.
// It does not care whether it is backed by a DB, files
// or any other means of storage (say a map[int] *types.User for testing),
// all it cares about that it will get a user if it calls store.GetUser(int).
// Or a mock, for that matter, which makes testing the handler
// **MUCH** easier.
func Getuser(store Storage) http.Handler {
// Most of error handling omitted for brevity
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
idParam := r.URL.Query().Get("cur1")
id,_ := strconv.Atoi(idParam)
user := store.Get(id)
if user == nil {
http.Error(w,"user not found", http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(user)
})
}
现在,只要您的数据访问层实现了
Storage
,您就可以轻松切换您正在使用的实际数据库,而无需在处理程序中更改一行代码。
显然,这只是开始。
旁注:我也不同意界面的设计,而且非常强烈。我的典型界面是这样的
// UserRepository is the data access layer for users.
// And yes, I come from Java... ;)
type UserCrudRepository interface {
// Create unsurprisingly persists a user.
// Since an ID might be created or a `create|modified_at`
// field populated, we return the newly created user.
// We also return an error which lets us decide on how to deal
// with the problem. Say `ErrUserAlreadyExists`... You get the picture.
Create(*types.User)(*types.User, error)
// Get should be self-explanatory.
Get(int) (*types.User, error)
// List too.
List() ([]*types.User, error)
// Update returns the newly modified user
// for pretty much the same reasons as in Get.
Update(*types.User) (*types.User, error)
// Delete deletes a user from the persistence.
Delete(int) error
// Sometimes, it might be necessary to delete more
// than one user. Instead of iterating and execute many operations,
// we can wrap this in a single transaction for databases, for example.
// Or, you can make it a convenience method around
//
// for _, id := range ids {
// if err := Delete(int); err != nil {
// // Handle error
// }
// }
//
// for more simplistic stores
DeleteBulk(ids []int) error
}