自动化生成代码的秘密

我作过两个自动化生成代码的项目,scaffoldredis-orm
scaffold 主要是经过数据库表定义来生成基于表的增删改查的基础管理工做;
redis-orm 是经过yaml的结构定义文件生成关系型数据库与redis的常规操做实现。
公司里还有一套微服务的自动化生成框架,可以快速的经过protobuf的定义文件生成项目的框架代码。c++

自动化生成代码有个最大的优势:减小程式化的编码。所谓程式化的编码就是,一般这些编码的工做量会随着业务量的增加线性增加,同时又是最没有技术含量的工做。因此经过开发自动化生成工具很是有必要,减小无谓的工做量同时大大提高工做效率,把你们解放出来作更有意义的事。git

不管是我写的自动化生成工具或是公司的微服务框架生成工具仍是其它一些官方工具,都有一个共同原理,所谓自动化生成代码的秘密,即程序员

经过结构化的元数据生成模式代码github

这句话中有两个关键词:golang

  • 结构化的元数据
    结构化的元数据的来源能够是:redis

    • 数据表定义
      例子: scaffold
    • 结构化的配置文件(yaml, toml 等等)
      例子: redis-orm
    • 服务接口定义(Thrift, ProtoBuffer等等)
      例子:grpcmicro
    • 程序代码中的类型、对象、接口等等
      例子:stringermock
  • 模式代码
    模式代码,即全部生成的代码是符合必定规律的,而这种规律就是基于元数据而言的。shell

最简单的例子数据库

官方的工具stringer就是一个自动化生成代码工具,主要用途是经过枚举值的变量名生成String函数接口,经常使用场景就是在定义程序状态码中使用。其中,结构化的元数据就是枚举类型的定义。编程

package codes

type Code uint32

//go:generate stringer -type=Code

const (
  OK Code = 0
  Canceled Code = 1
  Unknown Code = 2
  InvalidArgument Code = 3复制代码

这是一个简化版的GRPC状态码的例子,在文件所属目录下经过如下stringer命令便可生成代码文件code_string.gobash

$: stringer -type Code复制代码

生成的代码以下:

// Code generated by "stringer -type Code"; DO NOT EDIT

package codes

import "fmt"

const _Code_name = "OKCanceledUnknownInvalidArgument"

var _Code_index = [...]uint8{0, 2, 10, 17, 32}

func (i Code) String() string {
    if i >= Code(len(_Code_index)-1) {
        return fmt.Sprintf("Code(%d)", i)
    }
    return _Code_name[_Code_index[i]:_Code_index[i+1]]
}复制代码

原代码函数有一句注释的语句:

//go:generate stringer -type=Code复制代码

经过该语句,能够在命令行中执行以下命令,效果相同:

$: go generate复制代码

一个小技巧,在制做自动化生成代码工具的过程当中有时候会颇有用。

微服务框架的自动化

微服务如今很火,如何开发一个微服务框架的自动化生成工具呢?

首先,咱们要清楚什么是框架

框架是对接口的抽象

这是我我的对框架的总结,经过将项目中变化的部分经过接口抽象出来,提供给开发者,将不变的或者配置可变的放入框架中。

其实,grpc 已是一个简单的微服务框架了,只是功能比较单一,仅仅经过protobuf的定义生成客户端与服务端代码框架。它是怎么作到的?

管道的概念,作服务端的人都很是熟悉。能够用管道的概念类比一下grpc框架代码的生成过程

protoc | protoc-gen-go | plugin:grpc

protoc编译器经过读取protobuf协议与接口配置,输出结构化元数据给 protoc-gen-go,由它生成 go 代码,在protoc-gen-go中会用到 plugin:grpc 的插件实现grpc框架代码的定制生成。

固然, protoc-gen-go 调用 plugin:grpc 不是经过管道的方式。

要实现微服务框架的自动化的关键全在 plugin:grpc 中了。由于 plugin:grpc 就是一个代码生成器。你想要的全部心里戏所有能够在这里实现。包括:

  • 服务发现
  • 上下文定制
  • 错误处理
  • 日志
  • 统计

所有能够在框架里实现,仅仅暴露简单的接口供开发人员开发。

为了让生成代码更加精炼、可读性更强,共用的一些函数都会经过公用包的形式实现。

在安装GRPC的过程当中,有这样一条安装命令:

$: go get -u github.com/golang/protobuf/{proto,protoc-gen-go}复制代码

其中,包proto就是protoc生成go代码提供的公用包

结构化元数据

有时候阅读代码能够帮助咱们理解protobuf协议。在公司的微服务框架里用到了custom option. 在官方文档说,这个属性对于大部分开发者都是不会用到的。由于这个属性仅有在须要开发本身的框架代码时才会使用到。编写模式代码中,能够经过custom option控制框架代码的生成。

模式代码

除告终构化的元数据,模式代码的质量直接影响了项目自己的质量。模式代码保持精炼,可读性强都是一些基本要求。不贴代码了,具体代码参见grpc.go.

如何编写本身的Plugin,除了参考GRPC自己的Plugin实现,还能够参考这个项目
micro/protobuf.

自动化生成代码常见的坑

在开发自动化生成代码工具的过程当中,关键一步是编写模式代码。一般模式代码必定是经过不断的迭代才能达到所谓的完美。因此,在不断迭代的过程当中,就会出现,很痛苦的,改变接口

若是只是生成的代码改变接口可能影响面还比较小,只须要相应的修改调用方代码便可。可是若是生成代码中调用的公用包接口发生改变了,可能之前生成的代码就会发生故障。这也是我真实碰到过的一个坑。为了防止相似错误,能够经过版本控制的办法解决。

经过对仓库打tag,利用gopkg.io实现版本控制,是很是快捷且高效的解决办法.

如何用好自动化代码生成工具

用好自动化代码生成工具的关键,除了对生成代码自己要很熟悉外,还须要了解生成工具编写的模式代码。了解自动化代码生成工具的原理是很是必要的。

其实框架越强大,对于业务而言越有利,但对喜欢偷懒的程序员而言是不利的。因此利用偷懒来的时间,阅读框架代码很是必要。

归根结底,自动化编程是一项泛化编程技术,之前在c++中是件高端而隐秘的事,将程序执行期的代码移至编译期生成。现在,在go语言中,能够经过模板包template光明正大的干这件事了。

以上,就是我在开发和使用自动化代码生成工具中学到的些许经验,全当抛砖引玉,欢迎指教。

相关文章
相关标签/搜索