面向流的设计思想

作者 | 张逸

特别说明:本文包含大量代码片段,若要获得更好阅读观感,请点击文末“阅读原文”或访问我的博客。

响应式编程(Reactive Programming)的本质是异步非阻塞的高响应式处理,最核心思想则为Everything is stream,即针对流进行处理,这是其根本。从这个角度讲,我们可以将响应式编程的设计思想视为Stream-Oriented Design,即面向流的设计。

正如面向对象设计以对象为基本设计要素,函数式编程思想以函数为基本设计要素,响应式编程则应该以流为基本设计要素。这带来设计思想上根本的变化,包括:

  • 以流作为建模的元素
  • 流存在松耦合的上下游关系
  • 以流为重用的单位
  • 对流进行转换、运算、合并与拆分

在Rx框架中,一个流就是一个Observable或者Flowable。例如我们要统计网页的字数,则流的源头就是对网页内容的获取,而流就是Observable<String>类型的网页内容。至于统计操作,则需要经历分词、字数统计两个阶段,则可以视为是对流的转换与运算操作:

Flowable.fromFuture(pageContent)
    .flatMap(content -> Flowable.fromArray(s.split(" ")))
    .map(w -> new Pair<>(w, 1))
    .groupBy(Pair::getKey);

由于Rx框架提供了诸如merge、combineLatest、zip等操作符来完成多个流之间的组合,我们就可以分别建立各自的流,然后再利用这些操作符对其进行合并,或者反其道而行之。这样就能尽可能地分解出诸多原子的可重用的流。例如,针对UI的click操作以及response响应,我们就可以分别建立两个流,然后利用combineLatest进行组合。无论哪个流发射了数据,它都会将这两个流最近发射的数据组合起来,并按照指定的函数进行运算。

Akka Stream提出来的Graph更能体现流作为建模元素的思想。只要规划好我们的流程,思考组成这些流程的步骤的输入和输出,就可以分别将这些步骤分别建模为Source、Sink、Flow以及Fan-in、Fan-out和BidiFlow,如下图所示:

例如针对银行交易业务,如果我们需要执行如下流程:

  • 根据给定的账户编号获得所有的账户
  • 根据账户同时获得所有的银行交易(BackingTransaction)和结算交易(SettlementTransaction)
  • 获得这些交易后对交易进行验证
  • 验证后的数据分别用于用于审计和计算净值

我们对该流程进行领域建模时,实则可以绘制一个可以表达Akka Streams中Graph的可视化图:

通过这样的可视化图,我们就可以针对这些图中的节点建模为Akka Streams中的Graph Shape。至于流的广播与合并,则对应着框架的Broadcast Fan-out与Merge Fan-In。除了入口的accountNos是Source,以及用于最后的审计与净值计算作为Sink外,其余节点都是Flow类型。实现代码如下:

  val graph = RunnableGraph.fromGraph(GraphDSL.create(netTxnSink) { implicit b => ms =>
    import GraphDSL.Implicits._

    val accountBroadcast = b.add(Broadcast[Account](2))
    val txnBroadcast = b.add(Broadcast[Transaction](2))
    val merge = b.add(Merge[Transaction](2))

    val accounts = Flow[String].map(queryAccount(_, AccountRepository))
    val bankingTxns = Flow[Account].mapConcat(getBankingTransactions)
    val settlementTxns = Flow[Account].mapConcat(getSettlementTransactions)
    val validation = Flow[Transaction].map(validate)

    accountNos ~> accounts ~> accountBroadcast ~> bankingTxns ~> merge ~> validation ~> txnBroadcast ~> ms
                              accountBroadcast ~> settlementTxns ~> merge
    txnBroadcast ~> audit
    ClosedShape
  })

Scala语言由于提供了操作符重载,隐式转换等语法糖,在语言的表现能力更符合DSL的语义。例如代码中的~>符号非常清晰地表达出了数据流动的方向,流经什么样的节点。最关键的是,这些Flow定义彼此之间并没有强耦合关系,只要保证传输的数据是正确的,就可以利用组合操作符将Flow与Flow连接起来。这样的Flow同样是Lazy的,可以很好地得到高效重用。

因此,使用响应式编程,需得围绕“”为中心进行设计思考,并将其作为一个非常重要的重用元素进行组合。这也就是我所谓的面向流设计(Stream-Oriented Design)的想法来源。

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2018-03-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏恰童鞋骚年

[转] Agile Software Development 敏捷软件开发

  敏捷开发是一种软件开发方法,基于迭代和增量开发,通过自组织,跨团队,沟通协作完成开发工作。

1142
来自专栏程序人生

[技术] 谈谈Python

昨天的文章收获了不少有价值的回复。不少人发现了一个大bug,那就是「上帝的归上帝,撒旦的归撒旦」。囧死我了。脑手不同步这病怎么治啊~以后我写完文章争取好好复查一...

4105
来自专栏java架构师

设计模式学习笔记之桥接模式

这个模式在看书时,一直没想到更好的应用场景,由此感慨一下《设计模式之禅》这本书, 通过这本书,的确对各种模式有了个比较清晰的理解,甚至对模式的结构也能很明确。也...

3607
来自专栏深度学习那些事儿

学习C语言的必备书籍-从入门到精通

不同学校教材不通,大部分书都把C语言的基本内容讲出来了,不推荐谭浩强的C语言书,如果仅仅是当第一本C语言书是可以的。

3904
来自专栏灯塔大数据

技术 | Python从零开始系列连载(二十二)

为了解答大家学习Python时遇到各种常见问题,小灯塔特地整理了一系列从零开始的入门到熟练的系列连载,每周五准时推出,欢迎大家学积极学习转载~

1073
来自专栏python+iOS学习交流

2018最新最全BAT 全套高级iOS面试题以及面试资料强势来袭

一千个读者眼中有一千个哈姆雷特,一千名 iOS 程序员心目中就有一千套 iOS 高级面试题。本文就是笔者认为可以用来面试高级 iOS 程序员的面试题。

4092
来自专栏养码场

限时领取| 6GJavaScript高级视频,高级前端工程师必备武器!

之前场主分享了13G的JavaScript基础视频,共140集实战教学。没想到领取人数竟超过了5000+,着实让场主感受到了JavaScript教程的需求,及还...

842
来自专栏程序员互动联盟

【编程指导】如何写出无法维护的代码

读到一个非常有趣的文章,原文来自国外某网站,经过作者的翻译,读来非常有趣,反话正说,诙谐之中却道出了好多程序员不好的编程习惯。以下是翻译原文。 酷壳里有很多我觉...

3194
来自专栏java思维导图

跳槽时,这些Java面试题99%会被问到

工作多年以及在面试中,我经常能体会到,有些面试者确实是认真努力工作,但坦白说表现出的能力水平却不足以通过面试,通常是两方面原因:

2233
来自专栏平凡文摘

如果电脑技术最初是中国人发明的,那现在编程是不是就是中文的?

1745

扫码关注云+社区

领取腾讯云代金券