GoLang構造函數


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()
}

流程解讀

從上面兩個案例, 但從構造函數的設計上來看基本套路一致,同時可以顯而易見的看出,構造函數的思路.

  1. 聲明一個type(也有人習慣稱其為類),假定名稱為type A struct{}

  2. 為該type A struct{}准備幾個方法func Get()和func Set()

  3. 聲明一個獨立的函數,假定為名稱為NewTyepA(),

  4. 在NewTypeA()公共函數中返回, 實例化后的Type A struct的指針即可,

  5. 同時在NewTyeA()也可以為type A struct 指定各種初始化的操作

而后在其他文件中通過調用NewTypeA()即可調用TypeA中的所有方法

例如:

NewTypeA().Get()

就相當於:

// 偽代碼
typeA.Get()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM