传统的过程编码方式带来的弊端是显而易见,我们经常有这样的经验,一段时间不维护的代码或者别人的代码,突然拉回来看需要花费较长的时间,理解原来的思路,如果此时有个文档或者注释写的很好的话,可能花的时间会短一点,但是即便如此,很多调用关系也要反复确认才敢动手改动。下面是一断伪代码,描述过程编码方式:

func A(){
B()
C()
} func B(){
do something
D()
}
func C(){
do something
}
func D(){
do something
}
func main(){
A()
}

对照流式风格的写法:

NewStream().
Next(A).
Next(B).
Next(D).
Next(C).
Go()

当过程风格的代码调用关系复杂时,程序员需要谨慎仔细行事,相比较流式风格的代码比较清爽,主干清晰,尤其是应对需求变更的时候优势明显。

java8里借用lamda表达式实现了一套比较完美的流式编程风格,golang作为一个简洁的语言还没有官方的流式风格的包(可能早就有了,可能是我孤陋寡闻了)有点可惜了。

我参考了gorequest的代码,实现了一套相对比较通用的流式风格的包,实现原理是组成一个任务链表,每一个节点都保存了首节点和下一个节点以及该节点应该执行的回调函数指针,流式任务启动后从第一个节点开始,逐个执行,遇到异常则终止流式任务,直到执行到最后一个,结束任务链。先来看看代码吧:

package Stream

import (
"errors"
"fmt"
) /**
流式工作原理:
各个任务都过指针链表的方式组成一个任务链,这个任务链从第一个开始执行,直到最后一个
每一个任务节点执行完毕会将结果带入到下一级任务节点中。
每一个任务是一个Stream节点,每个任务节点都包含首节点和下一个任务节点的指针,
除了首节点,每个节都会设置一个回调函数的指针,用本节点的任务执行,
最后一个节点的nextStream为空,表示任务链结束。
**/ //定回调函数指针的类型
type CB func(interface{}) (interface{}, error) //任务节点结构定义
type Stream struct {
//任务链表首节点,其他非首节点此指针永远指向首节点
firstStream *Stream
//任务链表下一个节点,为空表示任务结束
nextStream *Stream
//当前任务对应的执行处理函数,首节点没有可执行任务,处理函数指针为空
cb CB
} /**
创建新的流
**/
func NewStream() *Stream {
//生成新的节点
stream := &Stream{}
//设置第一个首节点,为自己
//其他节点会调用run方法将从firs指针开始执行,直到next为空
stream.firstStream = stream
//fmt.Println("new first", stream)
return stream
} /**
流结束
arg为流初始参数,初始参数放在End方法中是考虑到初始参数不需在任务链中传递
**/
func (this *Stream) Go(arg interface{}) (interface{}, error) {
//设置为任务链结束
this.nextStream = nil
//fmt.Println("first=", this.firstStream, "second=", this.firstStream.nextStream)
//检查是否有任务节点存在,存在则调用run方法
//run方法是首先执行本任务回调函数指针,然后查找下一个任务节点,并调用run方法
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) {
//fmt.Println("run,args=", args)
//执行本节点函数指针
result, err := this.cb(arg)
//然后调用下一个节点的Run方法
if this.nextStream != nil && err == nil {
return this.nextStream.run(result)
} else {
//任务链终端,流式任务执行完毕
return result, err
}
}
func (this *Stream) Next(cb CB) *Stream {
//创建新的Stream,将新的任务节点Stream连接在后面
this.nextStream = &Stream{}
//设置流式任务链的首节点
this.nextStream.firstStream = this.firstStream
//设置本任务的回调函数指针
this.nextStream.cb = cb
//fmt.Println("next=", this.nextStream)
return this.nextStream
}

下面是一个流式的例子,这里以早上起床到出门上班的流程为例:

//起床
func GetUP(arg interface{}) (interface{}, error) {
t, _ := arg.(string)
fmt.Println("铃铃.......", t, "###到时间啦,再不起又要迟到了!")
return "醒着的状态", nil
} //蹲坑
func GetPit(arg interface{}) (interface{}, error) {
s, _ := arg.(string)
fmt.Println(s, "###每早必做的功课,蹲坑!")
return "舒服啦", nil
} //洗脸
func GetFace(arg interface{}) (interface{}, error) {
s, _ := arg.(string)
fmt.Println(s, "###洗脸很重要!")
return "脸已经洗干净了,可以去见人了", nil
} //刷牙
func GetTooth(arg interface{}) (interface{}, error) {
s, _ := arg.(string)
fmt.Println(s, "###刷牙也很重要!")
return "牙也刷干净了,可以放心的大笑", nil
} //吃早饭
func GetEat(arg interface{}) (interface{}, error) {
s, _ := arg.(string)
fmt.Println(s, "###吃饭是必须的(需求变更了,原来的流程里没有,这次加上)")
return "吃饱饱了", nil
} //换衣服
func GetCloth(arg interface{}) (interface{}, error) {
s, _ := arg.(string)
fmt.Println(s, "###还要增加一个换衣服的流程!")
return "找到心仪的衣服了", nil
} //出门
func GetOut(arg interface{}) (interface{}, error) {
s, _ := arg.(string)
fmt.Println(s, "###一切就绪,可以出门啦!")
return "", nil }
func main() {
NewStream().
Next(GetUP).
Next(GetPit).
Next(GetTooth).
Next(GetFace).
Next(GetEat).//需求变更了后加上的
Next(GetCloth).
Next(GetOut).
Go("2018年1月28日8点10分")
}

从上面的代码看,流式编码风格对于一个大的任务被分解成多个小任务后,在代码层面是非常直观的,不在费劲心思去查找到底那个调用了那个,另外对于需求的变更更容易了,上例中的吃早饭是第一个版本没有实现的,客户说了早上要吃饭,不然容易的胆结石,第二版要加上,我们需要完成吃饭的函数,然后加到响应的位置。相对过程编码简单了不少。

golang的极简流式编程实现的更多相关文章

  1. 20190827 On Java8 第十四章 流式编程

    第十四章 流式编程 流的一个核心好处是,它使得程序更加短小并且更易理解.当 Lambda 表达式和方法引用(method references)和流一起使用的时候会让人感觉自成一体.流使得 Java ...

  2. Stream流式编程

    Stream流式编程   Stream流 说到Stream便容易想到I/O Stream,而实际上,谁规定“流”就一定是“IO流”呢?在Java 8中,得益于Lambda所带来的函数式编程,引入了一个 ...

  3. JDK8新特性(二) 流式编程Stream

    流式编程是1.8中的新特性,基于常用的四种函数式接口以及Lambda表达式对集合类数据进行类似流水线一般的操作 流式编程分为大概三个步骤:获取流 → 操作流 → 返回操作结果 流的获取方式 这里先了解 ...

  4. Spark流式编程介绍 - 编程模型

    来源Spark官方文档 http://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#programm ...

  5. java8流式编程(一)

    传送门 <JAVA8开发指南>为什么你需要关注 JAVA8 <Java8开发指南>翻译邀请 Java8初体验(一)lambda表达式语法 Java8初体验(二)Stream语法 ...

  6. java8 流式编程

    为什么需要流式操作 集合API是Java API中最重要的部分.基本上每一个java程序都离不开集合.尽管很重要,但是现有的集合处理在很多方面都无法满足需要. 一个原因是,许多其他的语言或者类库以声明 ...

  7. Java8 新特性 —— Stream 流式编程

    本文部分摘自 On Java 8 流概述 集合优化了对象的存储,大多数情况下,我们将对象存储在集合是为了处理他们.使用流可以帮助我们处理对象,无需迭代集合中的元素,即可直接提取和操作元素,并添加了很多 ...

  8. java8 stream api流式编程

    java8自带常用的函数式接口 Predicate boolean test(T t) 传入一个参数返回boolean值 Consumer void accept(T t) 传入一个参数,无返回值 F ...

  9. 【响应式编程的思维艺术】 (1)Rxjs专题学习计划

    目录 一. 响应式编程 二. 学习路径规划 一. 响应式编程 响应式编程,也称为流式编程,对于非前端工程师来说,可能并不是一个陌生的名词,它是函数式编程在软件开发中应用的延伸,如果你对函数式编程还没有 ...

随机推荐

  1. codeForces 574b Bear and Three Musketeers

    一种巧妙到暴力方式,这题到抽象化:在一个无向图中,找一个度数和最小到三阶到圈. 首先对边进行枚举,这样确定了两个顶点,然后在对点进行枚举,找到一个可以构成三元圈到点,则计算他们到度数和. 最后保存最小 ...

  2. jquerymobile动态添加元素之后

      jquerymobile动态添加元素之后有些不能被正确渲染的解决方法:listview: 添加 jq(".detail").listview("refresh&quo ...

  3. Ubuntu下修改tomcat6默认的8080端口

    $ sudo vi /etc/tomcat6/server.xml   将 <Connector port="8080" protocol="HTTP/1.1&qu ...

  4. C# 时间格式 yyyy/mm/dd

    今天遇到个问题在C#中将日期格式设置为yyyy/MM/dd,我是这样写的: DateTime.Now.ToString("yyyy/MM/dd"); 可是获取到的日期还是显示yyy ...

  5. VM下redhat9.0不能上网

    近期本人在学习linux时,安装Red Hat Linux9后,可是上不了网,弄得查资料还得切换到虚拟机上去,特耗时间.还好没有疯掉! 首先,测试下你的linux看是否是这类问题,输入ping www ...

  6. Unity3D避免代码被反编译

    1.Unity编译后最终会将代码编译在dll里面,无论是ios还是Android解开包以后都可以拿到dll,路径在Data/Managed/Assembly-CSharp.dll 2.IOS其实不用做 ...

  7. 第二百四十二节,Bootstrap列表组面板和嵌入组件

    Bootstrap列表组面板和嵌入组件 学习要点: 1.列表组组件 2.面板组件 3.响应式嵌入组件 本节课我们主要学习一下 Bootstrap 的三个组件功能:列表组组件.面板组件. 响应 式嵌入组 ...

  8. 下列哪一个接口定义了用于查找、创建和删除EJB实例

    下列哪一个接口定义了用于查找.创建和删除EJB实例 A.Home B.Remote C.Local D.Message 解答:A remote接口定义了业务方法,用于EJB客户端调用业务方法. hom ...

  9. Oracle启动中,startup nomount、 startup mount 有什么差别?

    Oracle启动中,startup nomount. startup mount 有什么差别? 解答: startup nomount:启动实例,读取参数文件,分配内存空间,启动后台进程,打开跟踪文件 ...

  10. js禁止别人查看源码

    1.直接按F12 2.Ctrl+Shift+I查看 3.鼠标点击右键查看 4.Ctrl+u=view-source:+url 把以上三种状态都屏蔽掉就可以了,document有onkeydown(键盘 ...