[TOC]
GoLang构造函数
具体代码位置 构造函数 我之前的的另一个学习项目
另一篇笔记 Golang学习笔记 中的构造函数和复合声明部分也有描述和简单的实例
写这个笔记的原因,是因为前一阵子过同事的goLang代码, 看到他写的构造函数深感无奈.所以在这里记一下,我之前的案例.
构造函数源码
代码案例1
1package taskrunner 2 3//runner对象 4type Runner struct { 5 Controller controlChan //Dispatcher和Executor的生产者和消费者互相交互信息 6 Error controlChan //告知程序是否关闭资源 7 Data dataChan //真正的交互数据 8 dataSize int //传输的数据大小 9 longLived bool //是否是长期存在的资源(不回执行close()回收资源) 10 Dispatcher fn //分配器(生产者) 11 Executor fn //执行者(消费者) 12} 13 14//创建启动任务,模拟构造函数 15func NewRunner(size int, longLived bool, d fn, e fn) *Runner { 16 17 return &Runner{ 18 Controller: make(chan string, 1), //要带buffer 19 Error: make(chan string, 1), 20 Data: make(chan interface{}, size), 21 longLived: longLived, 22 dataSize: size, 23 Dispatcher: d, 24 Executor: e, 25 } 26} 27 28//开始分配任务(常驻任务),长时间等待Controller channel和Data channel的数据来做处理 29func (r *Runner) startDispatch() { 30 31 //声明匿名函数 32 defer func() { 33 //判断是否是要常驻内存,不需要的话就关闭所有channel 34 if !r.longLived { 35 close(r.Controller) 36 close(r.Data) 37 close(r.Error) 38 } 39 }() //没有这里的()该函数不会自动执行 40 41 //死循环不断处理消费者和生产者的channel中的数据 42 for { 43 select { 44 //读取Controller的channel中的数据,判断是生产者还是消费者 45 case c := <-r.Controller: 46 //生产者 47 if c == READY_TO_DISPATCH { 48 //把传入的数据,放入到生产者的回调函数中,同时判断回调函数的处理结果 49 err := r.Dispatcher(r.Data) 50 if err != nil { 51 //回调函数执行出错,通过传参, 指定关闭 52 r.Error <- CLOSE 53 } else { 54 //通过传参,切换为消费者 55 r.Controller <- READY_TO_EXECUTE 56 } 57 } 58 59 //消费者 60 if c == READY_TO_EXECUTE { 61 //把传入的数据,放入到消费者的回调函数中,同时判断回调函数的处理结果 62 err := r.Executor(r.Data) 63 if err != nil { 64 //回调函数执行出错,通过传参,指定关闭 65 r.Error <- CLOSE 66 } else { 67 //通过传参,切换为生产者 68 r.Controller <- READY_TO_DISPATCH 69 } 70 } 71 //读取channel中需要关闭的资源 72 case e := <-r.Error: 73 if e == CLOSE { 74 return 75 } 76 default: 77 78 } 79 80 } 81} 82 83//启动生产者和消费者模型 84func (r *Runner) StartAll() { 85 //开启生产者和消费者模型,同时预制状态,否则进程会僵死 86 r.Controller <- READY_TO_DISPATCH 87 //启动生产者 88 r.startDispatch() 89} 90
代理案例2
1package taskrunner 2 3import "time" 4 5type Worker struct { 6 /** 7 *ticker只要定义完成,从此刻开始计时,不需要任何其他的操作,每隔固定时间都会触发。 8 *timer定时器,是到固定时间后会执行一次 9 *如果timer定时器要每隔间隔的时间执行,实现ticker的效果,使用 func (t *Timer) Reset(d Duration) bool 10 */ 11 ticker *time.Ticker 12 runner *Runner 13} 14 15//新建一个进程 16func NewWorker(interval time.Duration, r *Runner) *Worker { 17 return &Worker{ 18 //NewTicker 返回一个新的 Ticker,该 Ticker 包含一个通道字段,并会每隔时间段 d 就向该通道发送当时的时间。它会调 19 //整时间间隔或者丢弃 tick 信息以适应反应慢的接收者。如果d <= 0会触发panic。关闭该 Ticker 可 20 //以释放相关资源。 21 ticker: time.NewTicker(interval * time.Second), 22 runner: r, 23 } 24} 25 26func (w *Worker) startWorker() { 27 //这里不能用range 否则会出现误差 28 for { 29 select { 30 case <-w.ticker.C: 31 go w.runner.StartAll() 32 } 33 } 34} 35 36func Start() { 37 //start video file cleaning 38 r := NewRunner(3, true, VideoClearDispatcher, VideoClearExecutor) 39 w := NewWorker(3, r) 40 go w.startWorker() 41}
流程解读
从上面两个案例, 但从构造函数的设计上来看基本套路一致,同时可以显而易见的看出,构造函数的思路.
-
声明一个type(也有人习惯称其为类),假定名称为type A struct{}
-
为该type A struct{}准备几个方法func Get()和func Set()
-
声明一个独立的函数,假定为名称为NewTyepA(),
-
在NewTypeA()公共函数中返回, 实例化后的Type A struct的指针即可,
-
同时在NewTyeA()也可以为type A struct 指定各种初始化的操作
而后在其他文件中通过调用NewTypeA()即可调用TypeA中的所有方法
例如:
NewTypeA().Get()
就相当于:
1// 伪代码 2typeA.Get()