前述

我们在写一个web模块或者api接口的时候,我们总是能够想到利用mvc模式,将model层,controller的action层分离, 使得我们的代码总是干净,可维护性强,注入新的服务简单.这一篇博客,我将探索在go中如何利用golang语言的特性,写一个干净,整洁,注入型强的代码.

一个简单的代码需求

  1. 起一个web server
  2. 实现用户注册接口
  3. 单元测试完备
  4. 后期新的需求,可以快速在现有代码的基础上完成

尽量编写testable的代码

我们总是希望,我们的testing中枚举用例不受过多服务的牵连,也不受过多业务逻辑的牵连
例如,用户有existscreate方法,那我们最好的方式则是创建一组这样的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,使得每一个对象自己的独立功能尽可能不耦合,这样后期的可维护性也就大大增加了.

标签: none

评论已关闭