专栏名称: 逸言
文学与软件,诗意地想念。
目录
相关文章推荐
程序员的那些事  ·  趣图:“微软穷疯了?上架的 ... ·  20 小时前  
程序员的那些事  ·  趣图:我和 DeepSeek 互换角色 ·  昨天  
码农翻身  ·  漫画 | ... ·  昨天  
OSC开源社区  ·  DeepSeek-V3满血版在国产沐曦GPU ... ·  3 天前  
51好读  ›  专栏  ›  逸言

面向流的设计思想

逸言  · 公众号  · 程序员  · 2018-03-08 22:13

正文

标签 | 响应式编程

作者 | 张逸


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

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

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

  • 以流作为建模的元素

  • 流存在松耦合的上下游关系

  • 以流为重用的单位

  • 对流进行转换、运算、合并与拆


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

1
2
3
4
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类型。实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  val graph = RunnableGraph.fromGraph(GraphDSL.create(netTxnSink) { implicit b => ms =>
    import GraphDSL.Implicits._

    val






请到「今天看啥」查看全文