本文摘自php中文网,作者藏色散人,侵删。
传统的过程编码方式带来的弊端是显而易见,我们经常有这样的经验,一段时间不维护的代码或者别人的代码,突然拉回来看需要花费较长的时间,理解原来的思路,如果此时有个文档或者注释写的很好的话,可能花的时间会短一点,但是即便如此,很多调用关系也要反复确认才敢动手改动。下面是一断伪代码,描述过程编码方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | func A(){
B()
C()
}
func B(){ do something
D()
}
func C(){ do something
}
func D(){ do something
}
func main(){
A()
}
|
对照流式风格的写法:
1 2 3 4 5 6 | NewStream().
Next(A).
Next(B).
Next(D).
Next(C).
Go()
|
当过程风格的代码调用关系复杂时,程序员需要谨慎仔细行事,相比较流式风格的代码比较清爽,主干清晰,尤其是应对需求变更的时候优势明显。
java8里借用lamda表达式实现了一套比较完美的流式编程风格,golang作为一个简洁的语言还没有官方的流式风格的包(可能早就有了,可能是我孤陋寡闻了)有点可惜了。
我参考了gorequest的代码,实现了一套相对比较通用的流式风格的包,实现原理是组成一个任务链表,每一个节点都保存了首节点和下一个节点以及该节点应该执行的回调函数指针,流式任务启动后从第一个节点开始,逐个执行,遇到异常则终止流式任务,直到执行到最后一个,结束任务链。先来看看代码吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package Stream
import ( "errors"
"fmt" )
//定回调函数指针的类型type CB func( interface {}) ( interface {}, error)//任务节点结构定义type Stream struct { //任务链表首节点,其他非首节点此指针永远指向首节点
firstStream *Stream
nextStream *Stream
}
func NewStream() *Stream {
stream := &Stream{}
stream.firstStream = stream
return stream
}
func (this *Stream) Go(arg interface {}) ( interface {}, error) {
this.nextStream = nil
if this.firstStream.nextStream != nil { return this.firstStream.nextStream.run(arg)
} else {
return nil, errors.New( "Not found execute node." )
}
}
func (this *Stream) run(arg interface {}) ( interface {}, error) {
result, err := this.cb(arg)
if this.nextStream != nil && err == nil { return this.nextStream.run(result)
} else {
return result, err
}
}
func (this *Stream) Next(cb CB) *Stream {
this.nextStream = &Stream{}
this.nextStream.firstStream = this.firstStream
this.nextStream.cb = cb
return this.nextStream
}
|
下面是一个流式的例子,这里以早上起床到出门上班的流程为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | t, _ := arg.(string)
fmt.Println( "铃铃......." , t, "###到时间啦,再不起又要迟到了!" ) return "醒着的状态" , nil
}
s, _ := arg.(string)
fmt.Println(s, "###每早必做的功课,蹲坑!" ) return "舒服啦" , nil
}
s, _ := arg.(string)
fmt.Println(s, "###洗脸很重要!" ) return "脸已经洗干净了,可以去见人了" , nil
}
s, _ := arg.(string)
fmt.Println(s, "###刷牙也很重要!" ) return "牙也刷干净了,可以放心的大笑" , nil
}
s, _ := arg.(string)
fmt.Println(s, "###吃饭是必须的(需求变更了,原来的流程里没有,这次加上)" ) return "吃饱饱了" , nil
}
s, _ := arg.(string)
fmt.Println(s, "###还要增加一个换衣服的流程!" ) return "找到心仪的衣服了" , nil
}
s, _ := arg.(string)
fmt.Println(s, "###一切就绪,可以出门啦!" ) return "" , nil
}
func main() {
NewStream().
Next(GetUP).
Next(GetPit).
Next(GetTooth).
Next(GetFace).
Next(GetEat).
Next(GetOut).
Go( "2018年1月28日8点10分" )
}
|
从上面的代码看,流式编码风格对于一个大的任务被分解成多个小任务后,在代码层面是非常直观的,不在费劲心思去查找到底那个调用了那个,另外对于需求的变更更容易了,上例中的吃早饭是第一个版本没有实现的,客户说了早上要吃饭,不然容易的胆结石,第二版要加上,我们需要完成吃饭的函数,然后加到响应的位置。相对过程编码简单了不少。
更多golang相关技术文章,请访问golang教程栏目!
以上就是golang的极简流式编程的详细内容,更多文章请关注木庄网络博客!!
相关阅读 >>
golang和c语言的区别是什么?
为什么我们从 docker 转向了 go?
golang实现二倍均值算法和抢红包的方法
gosumdb 设置私库
什么是golang中的interface
golang如何导入包
手撸golang 结构型设计模式 代理模式
详解 golang 通道 chan
go grpc 调试工具
关于golang中方法的receiver为指针和不为指针的区别
更多相关阅读请进入《golang》频道 >>
老貘
一个与时俱进的Go编程知识库。
转载请注明出处:木庄网络博客 » golang的极简流式编程