使用 Go 语言开发的哲学(误) | 青训营笔记

92 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第 7 篇笔记

使用 Go 语言开发的哲学

  • Go 语言从诞生之初就将效率和简单作为自己的主要目标,而使用 Go 语言开发,你只需要关注功能实现的逻辑,在开发的实际过程中进行补充就可以了。事实上,最好的做法就是围绕自己的逻辑,缺啥补啥。

  • 举个例子,你需要使用 Gin 写一个 Web 的后端,首先,你想到,你需要处理客户端传来的请求,那么你就可以通过 gin.Default() 拿取一个 gin.Engine 来处理,在这里你并不需要考虑其中的日志和崩溃恢复策略,是否重写日志和 Recovery 是之后需要考虑的事情。

  • 接下来,你想到,对于不同的路由需要有不同的处理器(controller)来处理,这些路由可以分为不同的组,比如 /user/login, /user/register/info/:id,这个时候,你通过 gin.GroupGET/POST 来快速地建立路由,但是你突然发现,你并没有可用的 controller,没关系,再建个包塞进去就好了。

  • 假设你在项目的根目录里新建了一个文件夹 controller/ 这里面放着 controller 包 (或者你觉得这个包名有点长,不如直接叫做 ctrl。没问题!包名和文件夹名不是强制相同的,你想怎么取就怎么取,怎么实用怎么来!)

  • 现在你的项目目录里有个 main.go、一个文件夹 controller/ ,里面放着一个 ctrl 包(除去 go.modgo.sum。或者你的项目是个使用了 go workspace 的项目,不过这都无所谓,先假设当前的项目是基于单个 go module 开发的),包里可能根据不同的服务写了不同名称的 .go 文件,文件里写了一些 controller

  • 好的,现在你有了 controller,路由的处理也写好了,但是看着膨胀起来的 main.go,多少觉得不够优雅……简单!抽取成一个函数,把 *gin.Engine 当做参数,来处理路由。或许还可以放到一个叫做 router.go 的文件中,而这个文件可以直接放在 main 包中!于是乎,你的 main.go 变成了这样:

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()

	route(r)
}

どのような優雅さ!

下面的是 router.go

package main

import (
	"l-gin/controller"

	"github.com/gin-gonic/gin"
)

func route(r *gin.Engine) {
	userGroup := r.Group("/user")
	{
		// 查看给定用户的信息
		userGroup.GET("/info/:id", controller.Info)

		// 用户登录
		userGroup.POST("/login", controller.Login)

		// 用户注册
		userGroup.POST("/register", controller.Register)

		// 关注操作
		userGroup.POST("/follow", controller.Follow)
                
                // ...
	}
}

  • 好了,到现在为止,路由已经建好了,那么我们就可以启动服务器了:

r.Run("127.0.0.1:80")

  • 等等等等,其他的东西呢?没事,我们慢慢来

  • 让我们从拿“用户登录”这个功能来开刀,首先,登录需要向服务器发送一些数据,我们需要将前端发送过来的数据包装一下,简单来说就是通过结构体简单封装一下。封装的过程很简单,这里就不展开了。

  • 好,现在我们得到了一个封装好的结构体,它就写在 controller/user.go 中。但是,我们不只有这一个服务,请求也不可能只有这一个类型,所以,我们可以再提取出来一个 req/ 里面放个叫做 req 的包,这个包放着我的用于封装请求的结构体

图片.png

  • 这里把 req 单独抽出来不止方便统一管理,同时也有另外一个好处,接着看下去你就知道了

  • 一般我们会将处理前端发来的数据的逻辑封装在 service 层(假设包名为 srv),而有时,我们会想直接把 controller 层封装好的结构体(或者结构体的指针,一般传指针是比较理想的)直接传到 service 层。也就是说,我们的 controller 调用了 service 层的函数,如果我们不将 req 抽取出来,那么 service 层想要使用 controller 层封装好的结构体,就只能 import controller 层的包!

  • 看出来了吗?!我们将业务逻辑提取到 service 层,又想直接使用 controller 层封装的结构体,所以 srv 导入了 ctrl,而在 ctrl 包,我们又导入了 srv,循环依赖了,而这是 Go 不允许的!

  • 所以,在可以且合理的范围内,进行适当地抽取,不只是让你的代码变得清晰,也会避免一些不必要的麻烦。

  • 好了,到目前为止,使用 Go 语言进行开发的哲学应该说的比较明白了,之后就是在实践中活用,读万卷书,行万里路(bushi)

最后希望 effective go, go effectively!

图片.png