在設計程序的許多應用場景中咱們會遇到大致分爲三個階段的任務流。node
第1、入口golang
一個或多個入口,等待阻塞的、或者主動請求方式的。微信
==============================框架
好比任務流須要接受來自於 HTTP 和 FTP 的應用請求,後續還有可能增長別的方式的接受請求。插件
第2、處理設計
多個入口能夠對應一個處理程序,也能夠對應多個處理程序。blog
==================================string
好比 HTTP 和 FTP 的多個入口程序須要對應一個和多個處理邏輯,一樣也面臨着增長處理程序的擴展性問題。it
第3、出口io
多個處理程序或者一個處理程序對應多個出口或者一個出口。
==================================
好比報警方式有郵件報警、微信報警等等,後續還有可能增長短信報警等等。
事實上能夠把絕大部分需求能夠抽象成上述思惟。
那麼對於這類問題咱們採起的擴展性要求是,拓展功能時改動少許原有代碼或者乾脆不改動原有代碼。在不考慮軟件重構層面,當新入職員工接手上位大哥的代碼時,我想最頭疼的就是增長需求,由於你不知道更改代碼會帶來什麼隱藏 BUG。
咱們老是理想能夠用"插件化"的思想來適應需求,寫好框架,而後分門別類,每個抽象出來的角色是一個大的容器,而後每次向這個容器中插一個個的插件,來一個需求,作一個插件插進去。來兩個需求,作兩個插件插一雙進去。
更"過度"的要求是插進去插件必須有一個按鈕來控制是否是啓用該插件,理由就是拔插一次插件太耗時了(對應代碼中的註釋 N 多行)。
總之一切的一切咱們想要的效果就是每個插件獨立工做,互不相應,增長可拓展性。這樣帶來的弊端就是可能有一部分的冗餘代碼,可是這樣也比交接出去工做讓人家每天在背後黑你要強的多吧。
那首先咱們先用 golang 來造出一個需求容器。
package need import ( "fmt" ) var ( Plugins map[string]Need ) func init() { Plugins = make(map[string]Need) } type Need interface { Flag() bool // 必須告訴容器是否啓動 Start() //必須有啓動方法 } func Start() { for name, plugin := range Plugins { if plugin.Flag() { go plugin.Start() fmt.Printf("啓動插件%s\n", name) } else { fmt.Printf("不啓動插件%s\n", name) } } } //每一個插件在初始化時必須註冊 func Register(name string, plugin Need) { Plugins[name] = plugin }
這個需求容器對插入本身的插件有要求!
若是缺乏 1,那麼就沒法實現插件生病就放病假。
若是缺乏 2,你說你不說你是幹啥的容器怎麼敢收你,萬一是販毒的咋辦~
若是缺乏 3,得與容器簽定合同,綁定勞動關係。
按照上述要求,咱們來實現一個插件一號。
package node import ( "fmt" "go_dev/plugin/need" ) func init() { p1 := plugin1{} need.Register("plugin1", p1) } type plugin1 struct { } func (p plugin1) Flag() bool { return true } func (p plugin1) Start() { fmt.Println("我是插件一號") }
如今是容器也有了,插件也有了,可是沒電跑不起來啊。。。,咱們如今再用 golang 給他提供個電源。
package main import ( "go_dev/plugin/need" _ "go_dev/plugin/node" ) func main() { need.Start() }