如何写出优雅的Golang代码(四)

 go教练   2019-07-30 16:21   84 人阅读  0 条评论

当我们在Go语言中处理错误相关的逻辑时,最重要的其实就是以下几点:

使用error实现错误处理-尽管这看起来非常啰嗦;将错误抛给上层处理-对于一个方法是否需要返回error也需要我们仔细地思考,向上抛出错误时可以通过errors.Wrap携带一些额外的信息方便上层进行判断;处理所有可能返回的错误-所有可能返回错误的地方最终一定会返回错误,考虑全面才能帮助我们构建更加健壮的项目;

 微信截图_20190612092925.png

我们在使用Go语言的这段时间,能够深刻地体会到它对于显式方法调用与错误处理的鼓励,这不仅能够帮助项目的其他开发者快速地理解上下文,也能够帮助我们构建更加健壮、容错性与可维护性更好的工程下面我们一起来看一下如何写出优雅的Golang代码第四部分

面向接口

面向接口编程是一个老生常谈的话题,接口 的作用其实就是为不同层级的模块提供了一个定义好的中间层,上游不再需要依赖下游的具体实现,充分地对上下游进行了解耦。

 微信截图_20190729100018.png

这种编程方式不仅是在 Go 语言中是被推荐的,在几乎所有的编程语言中,我们都会推荐这种编程的方式,它为我们的程序提供了非常强的灵活性,想要构建一个稳定、健壮的 Go 语言项目,不使用接口是完全无法做到的。

如果一个略有规模的项目中没有出现任何type ... interface的定义,那么作者可以推测出这在很大的概率上是一个工程质量堪忧并且没有多少单元测试覆盖的项目,我们确实需要认真考虑一下如何使用接口对项目进行重构。

单元测试是一个项目保证工程质量最有效并且投资回报率最高的方法之一,作为静态语言的 Golang,想要写出覆盖率足够(最少覆盖核心逻辑)的单元测试本身就比较困难,因为我们不能像动态语言一样随意修改函数和方法的行为,而接口就成了我们的救命稻草,写出抽象良好的接口并通过接口隔离依赖能够帮助我们有效地提升项目的质量和可测试性,我们会在下一节中详细介绍如何写单元测试。

package post var client *grpc.ClientConn func init() { var err error client, err = grpc.Dial(...) if err != nil { panic(err) } } func ListPosts() ([]*Post, error) { posts, err := client.ListPosts(...) if err != nil { return []*Post{}, err } return posts, nil }

上述代码其实就不是一个设计良好的代码,它不仅在init函数中隐式地初始化了 grpc 连接这种全局变量,而且没有将ListPosts通过接口的方式暴露出去,这会让依赖ListPosts的上层模块难以测试。

我们可以使用下面的代码改写原有的逻辑,使得同样地逻辑变得更容易测试和维护:

package post type Service interface { ListPosts() ([]*Post, error) } type service struct { conn *grpc.ClientConn } func NewService(conn *grpc.ClientConn) Service { return &service{ conn: conn, } } func (s *service) ListPosts() ([]*Post, error) { posts, err := s.conn.ListPosts(...) if err != nil { return []*Post{}, err } return posts, nil }通过接口Service暴露对外的ListPosts方法;使用NewService函数初始化Service接口的实现并通过私有的接口体service持有 grpc 连接;ListPosts不再依赖全局变量,而是依赖接口体service持有的连接;

当我们使用这种方式重构代码之后,就可以在main函数中显式的初始化 grpc 连接、创建Service接口的实现并调用ListPosts方法:

package main import ... func main() { conn, err = grpc.Dial(...) if err != nil { panic(err) } svc := post.NewService(conn) posts, err := svc.ListPosts() if err != nil { panic(err) } fmt.Println(posts) }

这种使用接口组织代码的方式在 Go 语言中非常常见,我们应该在代码中尽可能地使用这种思想和模式对外提供功能:

使用大写的Service对外暴露方法;使用小写的service实现接口中定义的方法;通过NewService函数初始化Service接口;

当我们使用上述方法组织代码之后,其实就对不同模块的依赖进行了解耦,也正遵循了软件设计中经常被提到的一句话 - 『依赖接口,不要依赖实现』,也就是面向接口编程。

在这一小节中总共介绍了 Go 语言中三个经常会打交道的『元素』-init函数、error和接口,我们在这里主要是想通过三个不同的例子为大家传达的一个主要思想就是尽量使用显式的(explicit)的方式编写 Go 语言代码。

以上就是今天给大家介绍的如何写出优雅的Golang代码如果你还想了解更多关于golang的知识技巧,可以继续关注我们http://www.fastgolang.com

本文地址:http://fastgolang.com/116.html
版权声明:本文为原创文章,版权归 go教练 所有,欢迎分享本文,转载请保留出处!

 发表评论


表情

还没有留言,还不快点抢沙发?