[TOC]
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()