Golang過(guò)程式編程
1、接口型函數(shù)
用函數(shù)實(shí)現(xiàn)接口,這樣在調(diào)用的時(shí)候就會(huì)非常簡(jiǎn)便,這一類型的函數(shù)就叫做接口型函數(shù);使用情景:僅限于只有一個(gè)函數(shù)簽名的接口。
-
原始使用,請(qǐng)看如下使用案例:
type Handle interface { Done (k , v interface{}) } func each(m map[interface{}]interface{}, h Handle) { if m != nil && len(m) > 0 { for k, v := range m { h.Done(k, v) } } }
上面案例,我們定義了僅有一個(gè)函數(shù)簽名Done (k , v interface{}) 的Handle類型接口,接著我們定義一個(gè)each函數(shù),接收兩個(gè)參數(shù):一個(gè)map、一個(gè)Handle類型,之后遍歷map, 調(diào)用Handle約束函數(shù)Done,操作map的key 和 value?,F(xiàn)在我們已經(jīng)定義好接口類型約束,接著我們要去使用了,請(qǐng)看如下使用案例:
type student struct {}
func (self *student) Done(k, v interface{}) {
fmt.Printf("student's name is: %s, age is: %d \n", k, v)
}
func main(){
m := make(map[interface{}]interface{})
m["sampsom"] = 25
m["jiny"] = 30
each(m, &student{})
}
仔細(xì)想想,以上使用案例有兩個(gè)有兩個(gè)缺陷:
a、因?yàn)榻涌谥皇且粋€(gè)約定的規(guī)則,不能直接使用,因此我們必須定義一個(gè)類型來(lái)實(shí)現(xiàn)這個(gè)接口,才能使用Handle約束的函數(shù)Done
b、既然是實(shí)現(xiàn)Handle類型接口,所以必須遵循Handle類型的約束規(guī)則,必須實(shí)現(xiàn)接口約束的方法Done。我們就不能夠隨心所欲的定義自己想要定義的更有意義的方法簽名
既然原始的接口使用方式,有上面的兩個(gè)缺陷,那么我們想要在約定的規(guī)則里,怎么才能隨心所欲的玩了,這就要我們的接口型函數(shù)來(lái)救場(chǎng)了
-
接口型函數(shù)的使用,請(qǐng)看如下案例:
type Handle interface { Done (k , v interface{}) } type HandleFunc func(f , v interface{}) func (f HandleFunc) Done(k , v interface{}) { h(k, v) }
請(qǐng)看上面案例,因?yàn)镠andleFunc類型,是一個(gè)和Handle接口類型約束的方法Done簽名一樣的函數(shù)類型,因此我們可以把Done方法的調(diào)用以HandleFunc函數(shù)調(diào)用來(lái)替換,即通過(guò)調(diào)用Done方法,來(lái)調(diào)用自身【HandleFunc使用Done方法包裝自己】。
現(xiàn)在只要是和HandleFunc也即是和Done一樣簽名的函數(shù),就可以使用HandleFunc(yourFunc),來(lái)強(qiáng)制轉(zhuǎn)換我們自己定義的更有意義的函數(shù)為HandleFunc類型函數(shù),從而持有Done方法,而調(diào)用自己了。請(qǐng)看下面我們?nèi)绾蝸?lái)使用:
func echoString(k , v interface{}) {
fmt.Printf("student's name is: %s, age is: %d \n", k, v)
}
func main(){
m := make(map[interface{}]interface{})
m["sampsom"] = 25
m["jiny"] = 30
each(m, HandleFunc(echoString))
}
現(xiàn)在我們的實(shí)現(xiàn)方式是不是更簡(jiǎn)單了,只要依據(jù)HandleFunc類型的簽名方式定義好我們自己想要定義的函數(shù),就可以直接使用了,而不用再去定義類型來(lái)實(shí)現(xiàn)Handle接口了,不過(guò)人都是愛(ài)偷懶的,每次調(diào)用時(shí)都要這樣HandleFunc(echoString)強(qiáng)制轉(zhuǎn)化,有沒(méi)有更好的方式了,請(qǐng)看下面我們的完整案例:
package main
import "fmt"
type Handle interface {
Done (k , v interface{})
}
func each(m map[interface{}]interface{}, h Handle) {
if m != nil && len(m) > 0 {
for k, v := range m {
h.Done(k, v)
}
}
}
//each函數(shù)包裝優(yōu)化
func EachFunc(m map[interface{}]interface{}, f func(k , v interface{})) {
each(m, HandleFunc(f))
}
//接口型函數(shù)實(shí)現(xiàn)
type HandleFunc func(k , v interface{})
func (h HandleFunc) Done(k , v interface{}) {
h(k, v)
}
func echoString(k , v interface{}) {
fmt.Printf("student's name is: %s, age is: %d \n", k, v)
}
//原始實(shí)現(xiàn)
type student struct {}
func (self *student) Done(k, v interface{}) {
fmt.Printf("student's name is: %s, age is: %d \n", k, v)
}
func main(){
m := make(map[interface{}]interface{})
m["sampsom"] = 25
m["jiny"] = 30
EachFunc(m, echoString)
}
接口型函數(shù)總結(jié)起來(lái)就三點(diǎn):
a、聲明只有一個(gè)方法簽名Done的接口Handle
b、聲明一個(gè)和接口方法簽名相同的函數(shù)類型HandleFunc
c、使函數(shù)類型HandleFunc實(shí)現(xiàn)接口類型Handle
2、middleware 中間件
在使用中間件的時(shí)候,我們的明白什么是中間件?為什么要使用中間件?在那些場(chǎng)景下適合使用中間件
2.1. 什么是中間件
首先,中間件的本質(zhì)就是:在執(zhí)行handler的前或者handler的后,先執(zhí)行一個(gè)自定義的handler而已,也就是要定義一個(gè)操作來(lái)包裝我們自定義的handler和我們要執(zhí)行的handler。
在這里,就要提出兩個(gè)概念:
?。?、業(yè)務(wù)代碼:也就是我們主要執(zhí)行的邏輯代碼,也即是將要執(zhí)行的handler
?。狻⒎菢I(yè)務(wù)代碼:也就是在執(zhí)行業(yè)務(wù)代碼之前或者之后要執(zhí)行的公用代碼,也就是我們自定義的共有handler
2.2. 為什么要使用中間件
既然明白了什么是中間件,那么我們就知道為什么要是用中間件了,使用中間件的目的就是剝離業(yè)務(wù)代碼和非業(yè)務(wù)代碼,實(shí)現(xiàn)解耦,從而減少遍地更改代碼的場(chǎng)景。
列如:
web業(yè)務(wù)的http請(qǐng)求,對(duì)于一個(gè)web項(xiàng)目我們會(huì)注冊(cè)很多路由,來(lái)匹配不同的業(yè)務(wù)場(chǎng)景,但是我們?nèi)绻獙?duì)請(qǐng)求耗時(shí)或者請(qǐng)求ip做統(tǒng)計(jì),就不能去更改每個(gè)路由對(duì)應(yīng)的業(yè)務(wù)代碼,而是把這一部分共有的操作作為非業(yè)務(wù)代碼剝離出來(lái),統(tǒng)一包裝執(zhí)行,這就要用到我們的中間件Middleware了
一般模式:
package main
import "fmt"
//中間件
type middleware func()
type ServiceHandle func()
//整合中間件和業(yè)務(wù)代碼
type Merge struct {
middlewareChain [] func()
serviceHandler ServiceHandle
}
func (self *Merge)Use(m middleware) {
self.middlewareChain = append(self.middlewareChain, m)
}
func (self *Merge)Add(sh ServiceHandle) {
self.serviceHandler = sh
for _, m := range self.middlewareChain {
m()
}
self.serviceHandler()
}
//業(yè)務(wù)代碼
func ServiceCodeHandle() {
fmt.Print("This is service code \n")
}
//非業(yè)務(wù)代碼
func NotServiceCodeHandle() {
fmt.Print("This is not service code \n")
}
func main(){
m := new(Merge)
m.Use(NotServiceCodeHandle)
m.Add(ServiceCodeHandle)
}
下面一http請(qǐng)求router舉例:
type middleware func(http.Handler) http.Handler
type Router struct {
middlewareChain [] func(http.Handler) http.Handler
mux map[string] http.Handler
}
func NewRouter() *Router{
return &Router{}
}
func (r *Router) Use(m middleware) {
r.middlewareChain = append(r.middlewareChain, m)
}
func (r *Router) Add(route string, h http.Handler) {
var mergedHandler = h
for i := len(r.middlewareChain) - 1; i >= 0; i-- {
mergedHandler = r.middlewareChain[i](mergedHandler)
}
r.mux[route] = mergedHandler
}
使用:
r = NewRouter()
r.Use(logger)
r.Use(timeout)
r.Use(ratelimit)
r.Add("/", helloHandler)
注意代碼中的 middleware 數(shù)組遍歷順序,和用戶希望的調(diào)用順序應(yīng)該是"相反"的.
3、validator 請(qǐng)求校驗(yàn)
使用第三方庫(kù)對(duì)請(qǐng)求參數(shù)做教研
https://github.com/go-playground/validator
import "gopkg.in/go-playground/validator.v9"
type RegisterReq struct {
// 字符串的 gt=0 表示長(zhǎng)度必須 > 0,gt = greater than
Username string `validate:"gt=0"`
// 同上
PasswordNew string `validate:"gt=0"`
// eqfield 跨字段相等校驗(yàn)
PasswordRepeat string `validate:"eqfield=PasswordNew"`
// 合法 email 格式校驗(yàn)
Email string `validate:"email"`
}
func validate(req RegisterReq) error {
err := validate.Struct(mystruct)
if err != nil {
doSomething()
}
...
}
4、golang如何實(shí)現(xiàn)插件化編程
代碼案例:
package main
import "fmt"
//聲明插件接口類型
type Plugin interface {
String()
}
//插件集合
type Plugins struct {
plist []Plugin
}
func (self *Plugins) init() {
self.plist = []Plugin{}
}
//插件注冊(cè)
func (self *Plugins) Register(p Plugin){
self.plist = append(self.plist, p)
}
type Student struct {
name string
sex string
age int
}
func (self *Student) String () {
fmt.Printf("Student's name is: %s, sex is: %s, age is: %d \n", self.name, self.sex, self.age)
}
func main(){
plugins := new(Plugins)
plugins.init()
p := &Student{
name: "sampson",
sex: "man",
age: 28,
}
plugins.Register(p)
for _, p := range plugins.plist{
p.String()
}
}
5、閉包
所謂閉包就是一個(gè)函數(shù)“捕獲”了和它在同一作用域的其他常量和變量。這就意味著閉包被調(diào)用的時(shí)候,不管程序在什么地方調(diào)用,閉包都能夠使用這些常量或者變量。只要閉包還存在,其所在作用域的變量或常量就存在
在go語(yǔ)言中所有匿名函數(shù)都是閉包。通常都是通過(guò)將一個(gè)閉包賦值給一個(gè)變量來(lái)使用閉包,或者將它放到一個(gè)數(shù)據(jù)結(jié)構(gòu)里(如映射或者切片),其主要的一種用法就是利用包裝函數(shù)來(lái)為被包裝函數(shù)預(yù)定一到多個(gè)參數(shù)。
type Student struct {
name string
sex string
age int
}
func string (s Student) func (){
return func() {
fmt.Printf("Student's name is: %s, sex is: %s, age is: %d \n", s.name, s.sex, s.age)
}
}
6、遞歸函數(shù)
遞歸函數(shù)就是調(diào)用自己的函數(shù),遞歸函數(shù)通常都有相同的結(jié)構(gòu):
一個(gè)跳出條件:跳出條件就是一個(gè)條件語(yǔ)句,例如:if語(yǔ)句
一個(gè)遞歸體:遞歸體就是函數(shù)自身所做的一些處理
通常函數(shù)開(kāi)始處都會(huì)有一個(gè)跳出條件來(lái)確保遞歸能夠正常結(jié)束
注意:遞歸函數(shù)最少的調(diào)用自身一次,而且調(diào)用時(shí)所傳入的參數(shù)一定不能和當(dāng)前函數(shù)傳入的一樣。常用于二叉樹(shù)等,如下案例:
//返回一個(gè)包含n個(gè)數(shù)字的斐波那契數(shù)列
func Fibonacci(n int) int {
if n < 2 {
return n
}
return Fibonacci(n-1) + Fibonacci(n-2)
}
7、運(yùn)行時(shí)候選擇函數(shù)
7.1 使用映射和函數(shù)引用來(lái)制造分支
func entry() {
var bi BusinessInstance
switch businessType {
case TravelBusiness:
bi = travelorder.New()
case MarketBusiness:
bi = marketorder.New()
default:
return errors.New("not supported business")
}
}
改為:
var businessInstanceMap = map[int]BusinessInstance {
TravelBusiness : travelorder.New(),
MarketBusiness : marketorder.New(),
}
func entry() {
bi := businessInstanceMap[businessType]
}
7.1 動(dòng)態(tài)的創(chuàng)建函數(shù)
當(dāng)我們有兩個(gè)或者更多的函數(shù)實(shí)現(xiàn)了相同的功能時(shí),比如使用了不同的算法,我們不希望在編譯的時(shí)候靜態(tài)的綁定到其中任何一個(gè)函數(shù)(列如:允許我們動(dòng)態(tài)的選擇他們來(lái)做性能測(cè)試或者回歸測(cè)試)。
var HasProperties func (string) bool
func init() {
if len(os.Args) >1 && (os.Args[1] == "-a" || os.Args[1] == "--asscii") {
os.Args = append(os.Args[:1], os.Args[2:]...)
HasProperties = func(s string) bool {
//業(yè)務(wù)邏輯代碼
return false
}
}else {
HasProperties = func(s string) bool {
//業(yè)務(wù)邏輯代碼
return true
}
}
}
8、泛型函數(shù)
即所有類型都能用的函數(shù),golang不支持泛型函數(shù),只有我們自己在代碼中使用類型斷言或者反射來(lái)兼容泛型
9、高階函數(shù)
所謂高階函數(shù)就是講一個(gè)或者多個(gè)其它函數(shù)作為自己的參數(shù),并在函數(shù)體里調(diào)用他們