GoLang構造函數
具體代碼位置 構造函數 我之前的的另一個學習項目
另一篇筆記 Golang學習筆記 中的構造函數和復合聲明部分也有描述和簡單的實例
寫這個筆記的原因,是因為前一陣子過同事的goLang代碼, 看到他寫的構造函數深感無奈.所以在這里記一下,我之前的案例.
構造函數源碼
代碼案例1
package taskrunner
//runner對象
type Runner struct {
Controller controlChan //Dispatcher和Executor的生產者和消費者互相交互信息
Error controlChan //告知程序是否關閉資源
Data dataChan //真正的交互數據
dataSize int //傳輸的數據大小
longLived bool //是否是長期存在的資源(不回執行close()回收資源)
Dispatcher fn //分配器(生產者)
Executor fn //執行者(消費者)
}
//創建啟動任務,模擬構造函數
func NewRunner(size int, longLived bool, d fn, e fn) *Runner {
return &Runner{
Controller: make(chan string, 1), //要帶buffer
Error: make(chan string, 1),
Data: make(chan interface{}, size),
longLived: longLived,
dataSize: size,
Dispatcher: d,
Executor: e,
}
}
//開始分配任務(常駐任務),長時間等待Controller channel和Data channel的數據來做處理
func (r *Runner) startDispatch() {
//聲明匿名函數
defer func() {
//判斷是否是要常駐內存,不需要的話就關閉所有channel
if !r.longLived {
close(r.Controller)
close(r.Data)
close(r.Error)
}
}() //沒有這里的()該函數不會自動執行
//死循環不斷處理消費者和生產者的channel中的數據
for {
select {
//讀取Controller的channel中的數據,判斷是生產者還是消費者
case c := <-r.Controller:
//生產者
if c == READY_TO_DISPATCH {
//把傳入的數據,放入到生產者的回調函數中,同時判斷回調函數的處理結果
err := r.Dispatcher(r.Data)
if err != nil {
//回調函數執行出錯,通過傳參, 指定關閉
r.Error <- CLOSE
} else {
//通過傳參,切換為消費者
r.Controller <- READY_TO_EXECUTE
}
}
//消費者
if c == READY_TO_EXECUTE {
//把傳入的數據,放入到消費者的回調函數中,同時判斷回調函數的處理結果
err := r.Executor(r.Data)
if err != nil {
//回調函數執行出錯,通過傳參,指定關閉
r.Error <- CLOSE
} else {
//通過傳參,切換為生產者
r.Controller <- READY_TO_DISPATCH
}
}
//讀取channel中需要關閉的資源
case e := <-r.Error:
if e == CLOSE {
return
}
default:
}
}
}
//啟動生產者和消費者模型
func (r *Runner) StartAll() {
//開啟生產者和消費者模型,同時預制狀態,否則進程會僵死
r.Controller <- READY_TO_DISPATCH
//啟動生產者
r.startDispatch()
}
代理案例2
package taskrunner
import "time"
type Worker struct {
/**
*ticker只要定義完成,從此刻開始計時,不需要任何其他的操作,每隔固定時間都會觸發。
*timer定時器,是到固定時間后會執行一次
*如果timer定時器要每隔間隔的時間執行,實現ticker的效果,使用 func (t *Timer) Reset(d Duration) bool
*/
ticker *time.Ticker
runner *Runner
}
//新建一個進程
func NewWorker(interval time.Duration, r *Runner) *Worker {
return &Worker{
//NewTicker 返回一個新的 Ticker,該 Ticker 包含一個通道字段,並會每隔時間段 d 就向該通道發送當時的時間。它會調
//整時間間隔或者丟棄 tick 信息以適應反應慢的接收者。如果d <= 0會觸發panic。關閉該 Ticker 可
//以釋放相關資源。
ticker: time.NewTicker(interval * time.Second),
runner: r,
}
}
func (w *Worker) startWorker() {
//這里不能用range 否則會出現誤差
for {
select {
case <-w.ticker.C:
go w.runner.StartAll()
}
}
}
func Start() {
//start video file cleaning
r := NewRunner(3, true, VideoClearDispatcher, VideoClearExecutor)
w := NewWorker(3, r)
go w.startWorker()
}
流程解讀
從上面兩個案例, 但從構造函數的設計上來看基本套路一致,同時可以顯而易見的看出,構造函數的思路.
-
聲明一個type(也有人習慣稱其為類),假定名稱為type A struct{}
-
為該type A struct{}准備幾個方法func Get()和func Set()
-
聲明一個獨立的函數,假定為名稱為NewTyepA(),
-
在NewTypeA()公共函數中返回, 實例化后的Type A struct的指針即可,
-
同時在NewTyeA()也可以為type A struct 指定各種初始化的操作
而后在其他文件中通過調用NewTypeA()即可調用TypeA中的所有方法
例如:
NewTypeA().Get()
就相當於:
// 偽代碼
typeA.Get()
