避免全域性變數
睡覺前好像確實不應該想問題,大半夜的思維活躍睡不著了。當然,那隻臭蚊子也有功勞,要是被我發現了我要滅了它。 不過,既然睡不著,那就起來寫篇部落格。
最近突然想到以前的一個專案,一個用來做token認證的微服務,當時為了快速實現,沒有嚴格遵守MVC,很多controller裡就有類似的 程式碼:
func XXXHandler() { db.Where("xxx = ?", 123).Find(&User) }
後來同事接入opentracing的時候,就很痛苦。當然了,來新公司之後也寫過類似的程式碼,主要是之前沒有想到特別好的解決方案,以及 嚴格遵守MVC的必要性。Go的ORM實在是太難用了,以至於無法完全的將物件和資料庫表解耦,如你所見,程式碼裡還到處都是SQL的影子。 如果是SQLAlchemy還真的很難看出這樣做有什麼不好。像上面的程式碼,至少有這麼幾個壞處:
-
暴露了具體SQL實現給外界,此處的外界是Controller。因為Go的ORM特別難用,裡面嵌入了大量的SQL語句,所以其實是和具體資料庫 強相關的。也就是說,這樣以來,假設啥時候要改個資料庫,那就完蛋了,因為到處充斥著這樣的程式碼,手都能改斷。
-
沒有重複利用程式碼。舉個例子,根據
user_id
拿User
資訊的程式碼,肯定到處都需要。如果所有的地方都是直接db.Where(xxx)
這樣的用法,就會造成和上面一條說到的一樣的問題。 -
無法對資料庫操作進行一些特定的,統一的操作。舉個例子,加tracing。如果我們把提取資料的函式封裝在M裡,那麼我們在每個方法 里加一行
defer BlablaTracing()
就可以達到我們的目的,但是像上面那樣,就不好辦了。
所以正確的方法應該是,遵循MVC。把資料庫操作封裝到M裡,例如,model層這樣寫:
type User struct{} func GetUserByID(id uint32) (*User, error) { user := User{} if err := db.Where("id = ?", id).Find(&user).Error; err != nil { return nil, err } else { return user, nil } }
然後,controller裡這樣寫:
func XXXHandler() { user, err := GetUserByID(user_id) xxxxxx }
所以說,有些懶,偷不得。為了不讓同事想開車從你身上碾幾遍,還是好好設計,好好想好少挖坑吧:grin: