服务对象模型在go中的应用
前述
我们在写一个web模块或者api接口的时候,我们总是能够想到利用mvc
模式,将model层,controller的action层分离, 使得我们的代码总是干净,可维护性强,注入新的服务简单.这一篇博客,我将探索在go中如何利用golang语言的特性,写一个干净,整洁,注入型强的代码.
一个简单的代码需求
- 起一个web server
- 实现用户注册接口
- 单元测试完备
- 后期新的需求,可以快速在现有代码的基础上完成
尽量编写testable的代码
我们总是希望,我们的testing中枚举用例不受过多服务的牵连,也不受过多业务逻辑的牵连
例如,用户有exists
和create
方法,那我们最好的方式则是创建一组这样的user相关的接口,大致代码如下:
type Repository interface {
Exists(email string) bool
Create(*Form) (*User, error)
}
func (m *Memstore) Exists(email string) bool {
return true
}
func (m *Memstore) Create(*Form) (*User, error) {
return &User{
Id: 1,
Email: "test@qq.com",
Password: "123",
}, nil
}
还有我们的user
type User struct {
Id int `json:"id"`
Email string `json:"email"`
Password string `json:"password"`
}
那么我们对服务的testing可以这样:
func TestMemstore_Exists(t *testing.T) {
type fields struct {
Users []User
}
type args struct {
email string
}
tests := []struct {
name string
fields fields
args args
want bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := &Memstore{
Users: tt.fields.Users,
}
if got := m.Exists(tt.args.email); got != tt.want {
t.Errorf("Exists() = %v, want %v", got, tt.want)
}
})
}
}
实现handler
我们绑定/register
接口到handle上面, 注意这里不是handleFunc, 我们需要做的是在handle上注入更多的toolings,例如: model模型,Validater组件等
// 他需要两个服务,一个create,一个exists
type RegisterHandler struct {
Repository
}
// 实现注册服务
func (r *RegisterHandler) ServeHTTP(http.ResponseWriter, *http.Request) {
}
这里有很重要的两点, 我们自己实现了http包的handler interface,我们将可testing的model模型绑定到了registerhandler上
我们在这里还可以绑定更多的组件
type RegistrationHandler struct {
Validator *validator.Validate
Repository
实现handler接口的绑定
func NewServer(addr string, r Repository) error {
mux := http.NewServeMux()
h := &RegisterHandler{r}
mux.Handle("register", h)
server := http.Server{
Addr: addr,
Handler: h,
}
err := server.ListenAndServe()
if err != nil {
return err
}
return nil
}
这里将h注册到handle上,并将Repository具体实现传递给handler.
总结
当新的需求来临时,代码耦合的可能性增加,因为没有明显的设计来保护。服务对象的方式我们传递给每一个handler,使得每一个对象自己的独立功能尽可能不耦合,这样后期的可维护性也就大大增加了.