拯救你的Go代碼

稍微有點標題黨,不過很短,能夠看mysql

背景

最近一直在參加開發公司新運營系統的引擎部分,寫了不少不少代碼,也逐漸產生了一些本身對此類業務的解決方案的簡單想法。運營系統乾的事,其實就是支持運營人員配置活動。所謂活動其實能夠簡單定義爲:一系列條件都知足就執行某個(些)動做。對應到程序員的黑話就是:一坨if else以後執行某(幾)個函數git

if else

說到寫if else,大多數程序員可能都會會心一笑,畢竟不論寫啥系統,代碼裏大部分語句仍是if else。業務系統就是對PM的需求堆if else,基礎服務就是對OS資源和網絡問題堆if else。愈來愈多的需求形成了代碼的膨脹,如何來管理這坨if else,就衍生出了設計模式:經過各類手段來把if else劃分到更小的粒度。固然,尚未任何一套方法論能夠解決if else複雜性的問題,由於if else反映的實際上是真實業務的需求,它其實表明了業務的複雜性。就像魯迅或者馬雲或者巴菲特說的:「不論用什麼方法,if else不會憑空消失,只會從一個地方轉移到另外一個地方」。可是,在面對特定的場景,特定的業務,代碼的閱讀性、可維護性以及擴展性是能夠獲得提高的。程序員

規則引擎

規則引擎不是一個新東西,並且已經存在了很長時間了,市面上也有各類規則引擎。好比你們經常使用的iptables,就是一種規則引擎,crontab也是一種規則引擎。咱們平時寫的業務邏輯,其實也是規則引擎,只是用一種不那麼明顯的方式來體現。好比:「若是重置的密碼沒有包含大小寫和標點符號,就不讓提交」,「若是用戶是抽獎送的VIP雖然不播廣告但給他彈窗」……這些業務邏輯,不管怎麼優化,要處理的代碼分支是必定存在的。規則引擎經過自定義的一套語法(DSL),提供了編程語言所不具有的語法糖,來最大限度減小開發量。同時大部分規則都須要支持用戶隨意配置,所以DSL大可能是解釋執行。用戶在後臺配置一條規則好比,遊戲中用戶在線1000分鐘後扣20的點卡:github

When
        {
           ?customer: Customer(totalTime >=1000);
        }
        Then
        {
           execute {?customer.setAmount(getAmount()-20.00);
        } 
複製代碼

市面上比較經常使用的幾款規則引擎都屬於很是重量級的,用戶必須去學習它的DSL才能使用,固然基本上都是JAVA的包。這有一篇文章是幾款規則引擎的比較。沒有深度用過,我也無法對此進行評價,但因爲其概念實在太多且使用場景受限,我以爲它始終是個臨時方案,畢竟「大道至簡」。並且DSL會越寫越複雜,真正用到語法糖的地方仍是少數,到最後變成另外一種蹩腳的並且領域受限的編程語言了。雖然如此,但因爲其規則可動態增長,老規則便於複用,對於提供通用解決方案的一些軟件公司來講,規則引擎仍是頗有吸引力的。畢竟代碼不須要動,加一些規則就能再次銷售。sql

可是對於進行業務開發的RD來講,其實需求很簡單,若是一個東西:express

  • 正確
  • 簡單
  • 能減小繁瑣的工做

那麼這即是一個有吸引力的解決方案,若是它能再快點兒,那就是一個絕妙的主意了。對於減小if else,我以爲Linq是一個很是好的方案。編程

Linq

Linq是C#中的一種常見技術,用過的都以爲爽。它就是一種語法糖,編譯器會編譯成真正的代碼而不是在運行期解釋執行,所以效率也很高。Linq代碼大概就是這樣:c#

// Specify the data source.
        int[] scores = new int[] { 97, 92, 81, 60 };

        // Define the query expression.
        IEnumerable<int> scoreQuery =
            from score in scores
            where score > 80
            select score;
複製代碼

能夠看到,在代碼中一些邏輯若是能這樣表達,那代碼將會變得很是簡單。linq基本上就是把sql語句搬到了C#中,惟一的區別就是它把select放在了最後而sql是在前面。這其實也是由於IDE智能提示的需求,而不是查詢結構自己要這麼設計。把select放在最後,IDE就知道你前面用過哪些變量,就能夠分析出你能夠select什麼從而進行提示。可是Linq是C#中的技術,和編譯器是強相關的,想移植到別的語言,仍是很不容易的。固然,社區也是有一些嘗試的,好比go的移植版linq-go。然而,你懂的,因爲go自己不支持泛型,編譯器也不支持linq語法,所以使用起來固然仍是比較蹩腳,並且確定是interface{}和類型推斷滿天飛了……設計模式

sql

其實咱們經常使用的sql中的where部分是完備的,Linq也是把sql搬到了C#中。換句話說,sql的where部分其實能夠組合任意的布爾邏輯。全部RD都會使sql,並且是常常使sql,既然想從編譯器的方向搞事情太麻煩(尤爲是go是一門以簡單著稱的語言,官方對於加關鍵字是深惡痛絕的,更別說語法糖了),那麼把mysql查詢功能幹掉,解釋執行where比較的那部分功能作成一個函數提供出來呢,好比查詢一我的是否是頂級程序員能夠這麼查:bash

sql := `sex='male' and ( dislike in ('girl', 'woman', 'female') or hobby in ('dress up', 'makeup') )`
複製代碼

可是因爲沒有表數據,那麼真正用於比較的數據就須要業務方本身來提供了,以上功能能夠寫爲:

func isTopProgrammer(userInfo User) bool {
    sql := `sex='male' and ( dislike in ('girl', 'woman', 'female') or hobby in ('dress up', 'makeup') )`
    ok,_ := yql.Match(sql, map[string]interface{}{
        "sex": userInfo.Sex,
        "dislike": userInfo.Dislike,
        "hobby": userInfo.Hobbies,
    })
    return ok
}
複製代碼

yql其實就是我最近搞的一個lib求star,求pr,求指導),幫你執行sql的比較。

若是這麼搞,其實業務邏輯中常常變更的部分能夠抽象到配置文件裏,好比:

// 先定義好不一樣case的處理函數
type handleFunc func(map[string]interface{}) error var handlers = map[int]handleFunc {
    1: sendEmail,
    2: sendCoupon,
    3: sendSms,
    4: sendAlert2Boss,
    5: runAway,
}
// 從當前請求中解析數據
data := resolveDataFromPostParams(request.Body)
// 從配置文件加載規則
rules := loadRuleFromConf()

//枚舉每條規則進行匹配,若是匹配成功則執行對應handler
for _,rule := range rules {
    success,err := yql.Match(rule.SQL, data)
    if nil != err || !success {
        continue
    }
    handler := handlers[rule.ID]
    handler(data)
    break
}
複製代碼

固然這種方式也有利有弊,好比說,把規則分離到配置文件中,在debug時得幾個文件之間跳來跳去地看代碼,比較蛋疼(少寫點bug就好了,哈哈)。優勢之一好比說能夠利用推送平臺實時更新配置文件,從而達到代碼熱更新的目的。

最後

其實yql還有不少應用場景(我感受),固然也有一些不足,好比功能還比較單一,解釋執行,快不快其實也沒和誰對比過……不過但願你們可以嘗試嘗試,若是可以提高工做效率,仍是不錯的。求關注,求star

相關文章
相關標籤/搜索