Loading

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()
posted @ 2021-07-30 15:25  我爱吃炒鸡  阅读(578)  评论(0编辑  收藏  举报