gin 服务编写

前面已经学习了路由和控制器,我们把逻辑都是写在控制器中的,我们把业务逻辑抽象成服务层,控制器只做请求处理,业务调用和数据响应。

1.服务定义

我们使用interface定义服务,具有那些功能,服务的实现可以是很多种,这样的好处可以无缝切换结构实现,让使用者关注”这个东西怎么用“,而不用关注具体是什么吧,统一的接口也可以让你的程序拆装更方便。

hello_contract.go

package hello

type HelloContract interface {
	SayHello(username string) string
}

hello_service.go

package hello

import "fmt"

type HelloService struct {
}

func (h *HelloService)SayHello(username string) string {
	return fmt.Sprintf("hello, %s", username)
}

由此我们服务已经编写完成,基于上一节的例子,我们改造一下:

test.go

package api

import (
	"cn.sockstack/gin_demo/requests"
	"github.com/gin-gonic/gin"
	"net/http"
)

func Test(c *gin.Context)  {
	//实例化一个TestRequest结构体,用于接收参数
	testStruct := requests.TestRequest{}

	//接收请求参数
	err := c.ShouldBind(&testStruct)

	//判断参数校验是否通过,如果不通过,把错误返回给前端
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"error": requests.Translate(err)})
		return
	}
    
	//调用HelloService
    var service hello.HelloContract

    //这里使用的是接口定义了变量
	service = &hello.HelloService{}
    //调用服务的方法处理业务
    result := service.SayHello(testStruct.Username)
    
	//校验通过,返回请求参数
	c.JSON(http.StatusOK, gin.H{"data": result})
}

运行结果:

{
	"data": "hello, sockstack"
}

2.切换服务

上面我们已经编写好了服务,但是感觉把service抽象一层和在控制器中写没什么区别,而且代码还少了。

其实上面只是一个简单的例子,当业务逻辑很复杂的时候,所有的代码都编码在控制器中,一来代码阅读起来困难,二来不方便测试,有时候代码出错了,debug都需要一段时间,而且功能修改的时候很容导致出错,并且不容易发现。

而且通过这种方式,当需求变更的时候,你只需要编写一个新的实现,替换调HelloService即可。

下面通过一个例子介绍服务切换:

同样的在hello_service.go中新建一个服务

package hello

import "fmt"

//原服务
type HelloService struct {
}

func (h *HelloService)SayHello(username string) string {
	return fmt.Sprintf("hello, %s", username)
}

//新服务
type GreeterService struct {
}

func (h *GreeterService)SayHello(username string) string {
	return fmt.Sprintf("hello, %s, how are you!", username)
}

然后我们修改test控制器

test.go

package api

import (
	"cn.sockstack/gin_demo/requests"
	"github.com/gin-gonic/gin"
	"net/http"
)

func Test(c *gin.Context)  {
	...
    
	//调用HelloService
    var service hello.HelloContract

    //这里使用的是接口定义了变量
	service = &hello.GreeterService{}
    //调用服务的方法处理业务
    result := service.SayHello(testStruct.Username)
    
	...
}

运行结果:

{
	"data": "hello, sockstack, how are you!"
}

到这里可能还没有太大的体会,不就是后面加了how are you吗,直接在原来的服务修改不是来的根块吗?其实在HelloService修改是很快,但是,当哪天,产品说需要需要修改回原来的服务版本的时候,如果是修改HelloService的方式,是不是该方法经常被修改,但是添加一个新的服务实现的时候,不需要做太多的事情,只要在控制器把HelloService修改成GreeterService即可,而且如果需要测试,可以把HelloService和GreeterService分开单独测试,互不影响。