问题 使用RX的最佳实践 - 返回Observable或接受观察者?


使用Reactive Extensions,我可以想到许多方法来模拟具有副作用/ IO的操作 - 比如订阅来自聊天室的消息。我可以接受参数(比如聊天室)和观察者,返回一个Disposable,即

Disposable SubscribeTo(string chatRoom, Observer<ChatMessage> observer)

给出参数,或返回一个Observable,即

Observable<ChatMessage> GetObservableFor(string chatRoom)

当返回一个Observable时,我还可以选择使其成为“热”还是“冷”,即在调用我的方法或订阅observable时执行实际订阅。另外,我可以使可观察的多路复用与否,即当Observable有多个用户时共享相同的基础订阅,或者每次订阅时发起新的请求。

对于订阅带参数的外部事件源的操作,是否有使用RX的最佳实践方法?


5704
2017-08-19 13:16


起源



答案:


回归*一世* Observable要好得多,因为您可以与其他运算符组合返回的IObservable。尝试将东西放入自定义的SubscribeTo方法对我来说似乎是一个坏主意,因为没有关于SubscribeTo的任何组合,所以你有点把自己画成一个角落。如果您返回IObservable,那么您可以稍后决定是否要发布/推迟等等...,只需使用现有的IO运算符即可。如果你在SubscribeTo中做到了,那就决定了,所有事情都必须分担后果。这种行为将被包含在SubscribeTo中,这违背了IO的目的......明确任何副作用。


6
2017-08-19 14:48



明确关于副作用,我之所以使用显式的“订阅”形式。你知道作为一个调用者,调用该函数会使事情发生,并且每次调用一次,从合同中可以明显看出这种情况。 - SoftMemes


答案:


回归*一世* Observable要好得多,因为您可以与其他运算符组合返回的IObservable。尝试将东西放入自定义的SubscribeTo方法对我来说似乎是一个坏主意,因为没有关于SubscribeTo的任何组合,所以你有点把自己画成一个角落。如果您返回IObservable,那么您可以稍后决定是否要发布/推迟等等...,只需使用现有的IO运算符即可。如果你在SubscribeTo中做到了,那就决定了,所有事情都必须分担后果。这种行为将被包含在SubscribeTo中,这违背了IO的目的......明确任何副作用。


6
2017-08-19 14:48



明确关于副作用,我之所以使用显式的“订阅”形式。你知道作为一个调用者,调用该函数会使事情发生,并且每次调用一次,从合同中可以明显看出这种情况。 - SoftMemes


IObservable 有一个名为Subscribe的方法,有大量的重载。您没有理由编写自定义的SubscribeTo方法。这是一种反模式。

var o = ChatRoom.ObservableFor("alt.buddha.short.fat.guy").Publish().RefCount()

使第一个订阅热门的多播然后在最后一个订阅被删除时冷。


3
2017-08-19 20:16



另一方面,从接受Observer并返回Disposable的东西中制作一个Observable非常容易,并且它会使副作用发生的地方更加明显...... - SoftMemes
如果您正在谈论“副作用”并且暗示建立管道的标准RX方式是错误的,那么您误读了太多的原教旨主义功能程序员博客。 - bradgonesurfing
BTW看看Observable.Create方法。 - bradgonesurfing
要完全清楚。任何有一个 Subscribe 方法应该是IObservable。了解这一点,你的问题就消失了。 - bradgonesurfing


我同意其他两个答案。我想进一步补充一些说明:

  1. 如果您的可观察序列是Hot,则几乎可以肯定它是共享的。因此,当你谈论Hot和Shared作为不同的东西时,听起来你可能会有些困惑。
  2. 我觉得这很好 订阅 有副作用。在功能编程中,关于避免副作用的大多数讨论都是关于 运营商 在管道中,而不是管道的建设。即不要在你的身上产生副作用 Where/Select/Take 等......运营商。这样做会产生刺耳的体验并导致不可预测的结果。这可以防止安全的成分,这是FP的基石。
  3. 将参数传递给返回可观察序列的方法是完全没问题的!您的样本是何时这样做的一个很好的例子。其他示例包括订阅频道,端点,会话,计时器等...
  4. 避免使用自定义实现 IObservable<T>。这只是禁忌。我已经使用Rx超过3年了,而且没有必要。即使使用Subject实现也很少见。正如布拉德所说,期待 Observable.Create 用于创建序列。*

总而言之,我建议你最终得到一个如下所示的界面:

IObservable<ChatMessage> MessagesFor(string chatRoom)

关于共享可观察序列的问题,您需要根据自己的要求和架构来决定。有时您可能会发现底层传输层将为您执行此操作,因此您无需对其进行编码。其他时候您可能会发现共享订阅意味着第二个+订阅者可能会错过仅在订阅时发生的消息(例如,State-Of-World)。你可以通过使用ReplaySubject作为你的MultiCaster来实现这个目标。这提出了其他问题;你准备好承担存储ChatMessages的可能无限制的费用吗?

*披露:链接到我自己的网站


2
2017-08-29 09:21