响应式编程,BloC,流
Reactive Programming, BLoC and Streams.
原文:https://www.didierboelens.com/2018/08/reactive-programming-streams-bloc/
What is a Stream? 什么是流?
In order to easily visualize the notion of Stream, simply consider a pipe with 2 ends, only one allowing to insert something into it. When you insert something into the pipe, it flows inside the pipe and goes out by the other end.
为了能够简单的描述流(Stream)的概念,你可以把它看成一根带有两端的水管(pipe),只允许一端流入,并且从另一端流出。
In Flutter, the pipe is called a Stream.
在flutter中,这根水管叫做流。
to control the Stream, we usually(*) use a StreamController
为了控制流,我们通常会使用流控制器(StreamController)
to insert something into the Stream, the StreamController exposes the “entrance", called a StreamSink, accessible via the sink property
为了往流里插入一些东西,流控制器(StreamController)暴露了通道,叫做流接收器(StreamSink),可以通过sink属性访问。
the way out of the Stream, is exposed by the StreamController via the stream property
流控制器(StreamController)通过stream属性暴露了流出去的通道。
What can be conveyed by a Stream? 流可以传递什么东西?
Everything and anything. From a value, an event, an object, a collection, a map, an error or even another Stream, any type of data may be conveyed by a Stream.
几乎任何东西都可以被流传递,值,时间,对象,集合,map,错误,甚至另一个流,任何类型的数据都可以被流传递。
How do I know that something is conveyed by a Stream? 我怎么知道一个东西被流传递了?
When you need to be notified that something is conveyed by a Stream, you simply need to listen to the stream property of the StreamController.
如果当某些东西被流传递时,你想获得通知,你只需要监听StreamController的stream属性。
When you define a listener, you receive a StreamSubscription object. This is via that StreamSubscription object that you will be notified that something happens at the level of the Stream.
当你定义一个listener时,你接收到一个StreamSubscription对象,通过这个StreamSubscription对象,你会被通知流发生的变化。
some data goes out from the stream, when some error has been sent to the stream, when the stream is closed.
当数据从流中出来,或某些错误被发送给流,或流被关闭时:
As soon as there is at least one active listener, the Stream starts generating events to notify the active StreamSubscription object(s) each time:
只要至少存在一个活跃的监听器,每次流都会产生事件来通知活跃的StreamSubscription对象。
The StreamSubscription object also allows you to: stop listening, pause, resume.
StreamSubscription对象允许你:停止监听,暂停,继续。
Is a Stream only a simple pipe? 流是一个简单的管道吗?
No, a Stream also allows to process the data that flows inside it before it goes out.
不,流允许数据处理后再流出。、
To control the processing of the data inside a Stream, we use a StreamTransformer, which is nothing but a function that “captures” the data that flows inside the Stream, does something with the data, the outcome of this transformation is also a Stream
为了在一个流中进行数据处理,我们使用StreamTransformer,是一个可以捕捉进入流的数据,并且对数据进行处理,数据经过转化后仍然是流。
You will directly understand from this statement that it is very possible to use several StreamTransformers in sequence.
可以依次使用多个StreamTransformers对数据进行处理。
A StreamTransformer may be used to do any type of processing, such as, e.g.:
一个StreamTransformer可以被用来进行任意的数据处理,比如:
-
filtering: to filter the data based on any type of condition, 过滤:基于某种条件过滤数据 -
regrouping: to regroup data, 重组:重新对数据进行组合 -
modification: to apply any type of modification to the data, 修改:对数据进行某种修改 -
inject data to other streams, 注入:往其它流中注入数据 -
buffering, 缓冲 -
processing: do any kind of action/operation based on the data, 处理:某种对数据的操作
Types of Streams 流的类型
Single-subscription Streams 单次订阅流
This type of Stream only allows a single listener during the whole lifetime of that Stream.
这种类型的流只允许有一个listener
It is not possible to listen twice on such Stream, even after the first subscription has been canceled.
即使第一个流的订阅被取消,也不可以在同一个流上监听两次。
Broadcast Streams 广播流
This second type of Stream allows any number of listeners.
这种类型的流允许任意数量的listeners
It is possible to add a listener to a Broadcast Stream at any moment. The new listener will receive the events, as of the moment it starts listening to the Stream.
可以随时添加一个广播流的listener。在开始监听流的时候,新的listener会接收事件。
Basic Examples 举例
This very first example shows a “Single-subscription” Stream, which simply prints the data which is input.
这个例子展示了单次订阅流,仅是把输入的数据打印出来。
import 'dart:async';
void main() {
//
// Initialize a "Single-Subscription" Stream controller
//
final StreamController ctrl = StreamController();
//
// Initialize a single listener which simply prints the data
// as soon as it receives it
//
final StreamSubscription subscription = ctrl.stream.listen((data) => print('$data'));
//
// We here add the data that will flow inside the stream
//
ctrl.sink.add('my name');
ctrl.sink.add(1234);
ctrl.sink.add({'a': 'element A', 'b': 'element B'});
ctrl.sink.add(123.45);
//
// We release the StreamController
//
ctrl.close();
}
This second example shows a “Broadcast” Stream, which conveys integer values and only prints the even numbers. To do so, we apply a StreamTransformer that filters (line #14) the values and only let the even numbers go through.
第二个例子展示了广播流,转化数字并且只打印偶数。我们通过StreamTransformer过滤数字,并且只让偶数通过。
import 'dart:async';
void main() {
//
// Initialize a "Broadcast" Stream controller of integers
//
final StreamController<int> ctrl = StreamController<int>.broadcast();
//
// Initialize a single listener which filters out the odd numbers and
// only prints the even numbers
//
final StreamSubscription subscription = ctrl.stream
.where((value) => (value % 2 == 0))
.listen((value) => print('$value'));
//
// We here add the data that will flow inside the stream
//
for(int i=1; i<11; i++){
ctrl.sink.add(i);
}
//
// We release the StreamController
//
ctrl.close();
}
RxDart
The RxDart package is an implementation for Dart of the ReactiveX API, which extends the original Dart Streams API to comply with the ReactiveX standards.
RxDart包是Dart的ReactiveX API的实现,扩展了Dart原生流的API,并且符合ReactiveX标准。
Dart-->RxDart Stream-->Observable StreamController-->Subject
RxDart as I just said, extends the original Dart Streams API and offers 3 main variations of the StreamController:
RxDart拓展了原生的Dart流API,并且提供了StreamController的3个变种。
PublishSubject 发布主题
The PublishSubject is a normal broadcast StreamController with one exception: stream returns an Observable rather than a Stream.
PublishSubject是正常的广播StreamController,但有一个例外:流返回一个Observable,而不是流。
As you can see, PublishSubject sends to a listener only the events that are added to the Stream after the time of the subscription.
如您所见,PublishSubject仅在订阅后将添加到Stream的事件发送到listener。
BehaviorSubject 行为主题
The BehaviorSubject is also a broadcast StreamController which returns an Observable rather than a Stream.
BehaviorSubject也是一个广播StreamController,它返回一个Observable,而不是流。
The main difference with a PublishSubject is that the BehaviorSubject also sends the very last event that was emitted to the listener that just subscribed.
与PublishSubject的主要区别在于BehaviorSubject还发送最后一个事件给刚刚订阅的listener。
ReplaySubject 重播主题
The ReplaySubject is also a broadcast StreamController which returns an Observable rather than a Stream.
ReplaySubject也是广播StreamController它返回一个Observable,而不是流。
A ReplaySubject, by default, sends all the events that were already emitted by the Stream to any new listener as the very first events.
一个ReplaySubject,默认情况下将流已经发送的事件作为最开始的事件,再次发送给新的listener。
What is Reactive Programming? 什么是响应式编程?
Reactive programming is programming with asynchronous data streams.
响应式编程是使用异步数据流进行编程。
In other words, everything from an event (e.g. tap), changes on a variable, messages, … to build requests, everything that may change or happen will be conveyed, triggered by a data stream.
换句话说,从事件(例如点击)、变量更改、消息到构建请求,所有可能更改或发生的事情都将被数据流触发。
In clear, all this means that, with reactive programming, the application:
很明显,这一切意味着,通过响应式编程,应用程序:
-
becomes asynchronous, 变得异步, -
is architectured around the notion of Streams and listeners, 围绕流和listeners的概念构建, -
when something happens somewhere (an event, a change of a variable …) a notification is sent to a Stream, 当某处发生某事(事件、变量的更改)时,会向Stream发送通知, -
if “somebody” listens to that Stream, it will be notified and will take appropriate action(s), whatever its location in the application. 如果“某人”侦听该Stream,它将收到通知并采取适当的行动,无论它在应用程序中的位置如何。
There is no longer any tight coupling between components.
组件之间不再有任何紧密耦合。
In short, when a Widget sends something to a Stream, that Widget does no longer need to know:
简而言之,当 Widget 向Stream发送某些内容时,该 Widget不再需要知道
-
what is going to happen next, 接下来会发生什么, -
who might use this information (no one, one or several Widgets…) 谁可能会使用这些信息(没有一个,一个或几个小部件......) -
where this information might be used (nowhere, same screen, another one, several ones…), 可能使用此信息的地方(无处,同一屏幕,另一个,几个……), -
when this information might be used (almost directly, after several seconds, never…). 何时可能使用此信息(几乎是直接使用,几秒钟后,从不……)。
that Widget does only care about its own business, that’s all !!
那个Widget只关心自己的生意,仅此而已!!
The BLoC Pattern BLoC 模式
BLoC stands for Business Logic Component.
BLOC代表业务逻辑组件。
In short, the Business Logic needs to:
简而言之,业务逻辑需要:
-
be moved to one or several BLoCs, 移至一个或多个BLoC, -
be removed as much as possible from the Presentation Layer. In other words, UI components should only worry about the UI things and not about the business, 尽可能多地从展示层中删除。换句话说,UI 组件应该只关心 UI 的事情而不是业务 -
rely on exclusive use of Streams for both input (Sink) and output (stream), 依赖于对输入(Sink)和输出(stream)的Streams 的独占使用, -
remain platform independent, 保持平台独立, -
remain environment independent. 保持环境独立。
In fact, the BLoC pattern was initially conceived to allow to reuse the very same code independently of the platform: web application, mobile application, back-end.
事实上,BLoC模式最初的构想是允许独立于平台重用完全相同的代码:Web 应用、移动App、后端。
The BLoC pattern makes use of the notions that we just discussed above: Streams.
BLoC 模式使用了流的概念。
-
Widgets send events to the BLoC via Sinks, 小部件通过Sinks向 BLoC发送事件, -
Widgets are notified by the BLoC via streams, BLoC 通过流通知小部件, -
the business logic which is implemented by the BLoC is none of their concern. BLoC 实现的业务逻辑与他们无关。
Thanks to the decoupling of the Business Logic from the UI:
得益于业务逻辑与 UI 的解耦:
-
we may change the Business Logic at any time, with a minimal impact on the application, 我们可以随时更改业务逻辑,对应用程序的影响最小, -
we may change the UI without any impact on the Business Logic, 我们可以在不影响业务逻辑的情况下更改 UI, -
it is now much easier to test the Business Logic. 现在可以更容易地测试业务逻辑。