自動(dòng)化生成代碼的秘密

我做過兩個(gè)自動(dòng)化生成代碼的項(xiàng)目,scaffoldredis-orm。
scaffold 主要是通過數(shù)據(jù)庫表定義來生成基于表的增刪改查的基礎(chǔ)管理工作;
redis-orm 是通過yaml的結(jié)構(gòu)定義文件生成關(guān)系型數(shù)據(jù)庫與redis的常規(guī)操作實(shí)現(xiàn)。
公司里還有一套微服務(wù)的自動(dòng)化生成框架,能夠快速的通過protobuf的定義文件生成項(xiàng)目的框架代碼。

自動(dòng)化生成代碼有個(gè)最大的優(yōu)點(diǎn):減少程式化的編碼。所謂程式化的編碼就是,通常這些編碼的工作量會(huì)隨著業(yè)務(wù)量的增長(zhǎng)線性增長(zhǎng),同時(shí)又是最沒有技術(shù)含量的工作。所以通過開發(fā)自動(dòng)化生成工具非常有必要,減少無謂的工作量同時(shí)大大提升工作效率,把大家解放出來做更有意義的事。

不論是我寫的自動(dòng)化生成工具或是公司的微服務(wù)框架生成工具還是其它一些官方工具,都有一個(gè)共同原理,所謂自動(dòng)化生成代碼的秘密,即

通過結(jié)構(gòu)化的元數(shù)據(jù)生成模式代碼

這句話中有兩個(gè)關(guān)鍵詞:

  • 結(jié)構(gòu)化的元數(shù)據(jù)
    結(jié)構(gòu)化的元數(shù)據(jù)的來源可以是:

    • 數(shù)據(jù)表定義
      例子: scaffold
    • 結(jié)構(gòu)化的配置文件(yaml, toml 等等)
      例子: redis-orm
    • 服務(wù)接口定義(Thrift, ProtoBuffer等等)
      例子:grpc、micro
    • 程序代碼中的類型、對(duì)象、接口等等
      例子:stringer、mock
  • 模式代碼
    模式代碼,即所有生成的代碼是符合一定規(guī)律的,而這種規(guī)律就是基于元數(shù)據(jù)而言的。

最簡(jiǎn)單的例子

官方的工具stringer就是一個(gè)自動(dòng)化生成代碼工具,主要用途是通過枚舉值的變量名生成String函數(shù)接口,常用場(chǎng)景就是在定義程序狀態(tài)碼中使用。其中,結(jié)構(gòu)化的元數(shù)據(jù)就是枚舉類型的定義。

package codes

type Code uint32

//go:generate stringer -type=Code

const (
  OK Code = 0
  Canceled Code = 1
  Unknown Code = 2
  InvalidArgument Code = 3
)

這是一個(gè)簡(jiǎn)化版的GRPC狀態(tài)碼的例子,在文件所屬目錄下通過以下stringer命令即可生成代碼文件code_string.go。

$: 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]]
}

原代碼函數(shù)有一句注釋的語句:

//go:generate stringer -type=Code

通過該語句,可以在命令行中執(zhí)行如下命令,效果相同:

$: go generate

一個(gè)小技巧,在制作自動(dòng)化生成代碼工具的過程中有時(shí)候會(huì)很有用。

微服務(wù)框架的自動(dòng)化

微服務(wù)現(xiàn)在很火,如何開發(fā)一個(gè)微服務(wù)框架的自動(dòng)化生成工具呢?

首先,我們要清楚什么是框架?

框架是對(duì)接口的抽象

這是我個(gè)人對(duì)框架的總結(jié),通過將項(xiàng)目中變化的部分通過接口抽象出來,提供給開發(fā)者,將不變的或者配置可變的放入框架中。

其實(shí),grpc 已經(jīng)是一個(gè)簡(jiǎn)單的微服務(wù)框架了,只是功能比較單一,僅僅通過protobuf的定義生成客戶端與服務(wù)端代碼框架。它是怎么做到的?

管道的概念,做服務(wù)端的人都非常熟悉。可以用管道的概念類比一下grpc框架代碼的生成過程。

protoc | protoc-gen-go | plugin:grpc

protoc編譯器通過讀取protobuf協(xié)議與接口配置,輸出結(jié)構(gòu)化元數(shù)據(jù)給 protoc-gen-go,由它生成 go 代碼,在protoc-gen-go中會(huì)用到 plugin:grpc 的插件實(shí)現(xiàn)grpc框架代碼的定制生成。

當(dāng)然, protoc-gen-go 調(diào)用 plugin:grpc 不是通過管道的方式。

要實(shí)現(xiàn)微服務(wù)框架的自動(dòng)化的關(guān)鍵全在 plugin:grpc 中了。因?yàn)?plugin:grpc 就是一個(gè)代碼生成器。你想要的所有內(nèi)心戲全部可以在這里實(shí)現(xiàn)。包括:

  • 服務(wù)發(fā)現(xiàn)
  • 上下文定制
  • 錯(cuò)誤處理
  • 日志
  • 統(tǒng)計(jì)

全部可以在框架里實(shí)現(xiàn),僅僅暴露簡(jiǎn)單的接口供開發(fā)人員開發(fā)。

為了讓生成代碼更加精煉、可讀性更強(qiáng),共用的一些函數(shù)都會(huì)通過公用包的形式實(shí)現(xiàn)。

在安裝GRPC的過程中,有這樣一條安裝命令:

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

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

** 結(jié)構(gòu)化元數(shù)據(jù) **

有時(shí)候閱讀代碼可以幫助我們理解protobuf協(xié)議。在公司的微服務(wù)框架里用到了custom option. 在官方文檔說,這個(gè)屬性對(duì)于大部分開發(fā)者都是不會(huì)用到的。因?yàn)檫@個(gè)屬性僅有在需要開發(fā)自己的框架代碼時(shí)才會(huì)使用到。編寫模式代碼中,可以通過custom option控制框架代碼的生成。

** 模式代碼 **

除了結(jié)構(gòu)化的元數(shù)據(jù),模式代碼的質(zhì)量直接影響了項(xiàng)目本身的質(zhì)量。模式代碼保持精煉,可讀性強(qiáng)都是一些基本要求。不貼代碼了,具體代碼參見grpc.go.

如何編寫自己的Plugin,除了參考GRPC本身的Plugin實(shí)現(xiàn),還可以參考這個(gè)項(xiàng)目
micro/protobuf.

自動(dòng)化生成代碼常見的坑

在開發(fā)自動(dòng)化生成代碼工具的過程中,關(guān)鍵一步是編寫模式代碼。通常模式代碼一定是通過不斷的迭代才能達(dá)到所謂的完美。所以,在不斷迭代的過程中,就會(huì)出現(xiàn),很痛苦的,改變接口

如果只是生成的代碼改變接口可能影響面還比較小,只需要相應(yīng)的修改調(diào)用方代碼即可。但是如果生成代碼中調(diào)用的公用包接口發(fā)生改變了,可能以前生成的代碼就會(huì)發(fā)生故障。這也是我真實(shí)碰到過的一個(gè)坑。為了防止類似錯(cuò)誤,可以通過版本控制的辦法解決。

通過對(duì)倉庫打tag,利用gopkg.io實(shí)現(xiàn)版本控制,是非??旖萸腋咝У慕鉀Q辦法.

如何用好自動(dòng)化代碼生成工具

用好自動(dòng)化代碼生成工具的關(guān)鍵,除了對(duì)生成代碼本身要很熟悉外,還需要了解生成工具編寫的模式代碼。了解自動(dòng)化代碼生成工具的原理是非常必要的。

其實(shí)框架越強(qiáng)大,對(duì)于業(yè)務(wù)而言越有利,但對(duì)喜歡偷懶的程序員而言是不利的。所以利用偷懶來的時(shí)間,閱讀框架代碼非常必要。

歸根結(jié)底,自動(dòng)化編程是一項(xiàng)泛化編程技術(shù),以前在c++中是件高端而隱秘的事,將程序執(zhí)行期的代碼移至編譯期生成。如今,在go語言中,可以通過模板包template光明正大的干這件事了。

以上,就是我在開發(fā)和使用自動(dòng)化代碼生成工具中學(xué)到的些許經(jīng)驗(yàn),全當(dāng)拋磚引玉,歡迎指教。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 我做過兩個(gè)自動(dòng)化生成代碼的項(xiàng)目,scaffold和redis-orm。scaffold 主要是通過數(shù)據(jù)庫表定義來生...
    ezbuy研發(fā)閱讀 2,128評(píng)論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,697評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,351評(píng)論 25 708
  • 又回到了夢(mèng)想開始的地方,又見到了一群眼睛閃著光亮的年輕人。看著歡聲雀躍的他們,仿佛自己又變成了當(dāng)年那個(gè)干什么都不知...
    時(shí)間的玫瑰_閱讀 421評(píng)論 1 6
  • 張鈞甯素有“臺(tái)灣第一氣質(zhì)美女”之稱,因高學(xué)歷而被冠以“演藝圈女學(xué)霸”的標(biāo)簽,一向以知性大方、獨(dú)立自信的優(yōu)質(zhì)形象示人...
    水墨無痕1閱讀 657評(píng)論 0 2

友情鏈接更多精彩內(nèi)容