在Rx中保持可观察状态的最佳方法

时间:2017-01-31 17:24:02

标签: rx-java system.reactive

我有一个关于什么是更好的RxJava模式以保持可观察状态的问题。

为了简单起见,我们假设我们有一个StateManager类需要跟踪某个状态(让我们假设它是一个简单的布尔标志)系统并以可观察的方式公开它。因此,它将采用如下方法:

class StateManager {
    Observable<Boolean> state();
    ...
}

这位经理的生命周期很长,可能有多个客户&#34; (例如,观点,其他经理等),可以随时订阅或取消订阅。国家将根据一些内部事件进行更改。

最明显的处理方法是将状态保存在消费者直接挂钩的BehaviourSubject中:

class StateManager {

    Subject mStateSubject = BehaviourSubject.create(true);        

    Observable<Boolean> state() {
        return mStateSubject.asObservable();
    }    
    ...
}

有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

Subjects可能是使用反应库的最不理想的方法,尽管它肯定可行。

功能反应式编程在没有状态的情况下效果最佳。 Subjects是一种国家形式。我建议更改代码,以便将Observable定义为功能操作符的组合。这样可以轻松测试和管理您的observable发出的消息。

我更像是一名C#开发人员,所以我希望你能原谅不同的语法。这是一个例子:

void Main()
{
    var tracker = new AddTracker();
    tracker.getSums().Subscribe(i => Console.WriteLine(i));
    Observable.Interval(TimeSpan.FromMilliseconds(100))
        .Timestamp()
        .Select(t => t.Timestamp.Second)
        .Take(20)
        .Subscribe(i => tracker.setA(i % 7));

    Observable.Interval(TimeSpan.FromMilliseconds(75))
        .Timestamp()
        .Select(t => t.Timestamp.Millisecond)
        .Take(30)
        .Subscribe(i => tracker.setB(i % 9));

}

public class AddTracker
{
    private readonly ISubject<int> _a;
    private readonly ISubject<int> _b;
    private readonly IObservable<int> _sums;
    private readonly IDisposable _dummySub;

    public AddTracker()
    {
        _a = new BehaviorSubject<int>(0);
        _b = new BehaviorSubject<int>(0);
        _sums = _a
            .CombineLatest(_b, (a, b) => a + b)
            .Replay(1)
            .RefCount();
        _dummySub = _sums.Subscribe(_ => { });
    }

    public void setA(int value)
    {
        _a.OnNext(value);
    }

    public void setB(int value)
    {
        _b.OnNext(value);
    }

    public IObservable<int> getSums()
    {
        return _sums;
    }
}

在C#land中,您可以将_a_b主题换成事件,这是一个温和的改进。我知道Java中没有一流的事件,所以我不确定会翻译。

然而,更基本的是,在C#和Java中,您应该问的问题是......导致setAsetB调用的原因是什么?你能用这个代替它们吗?

void Main()
{
    var aStream = Observable.Interval(TimeSpan.FromMilliseconds(100))
        .Timestamp()
        .Select(t => t.Timestamp.Second)
        .Take(20);

    var bStream = Observable.Interval(TimeSpan.FromMilliseconds(75))
        .Timestamp()
        .Select(t => t.Timestamp.Millisecond)
        .Take(30);

    var tracker = new AddTracker(aStream, bStream);
    tracker.getSums().Subscribe(i => Console.WriteLine(i));

}

public class AddTracker
{
    private readonly IObservable<int> _sums;
    private readonly IDisposable _dummySub;

    public AddTracker(IObservable<int> a, IObservable<int> b)
    {
        _sums = a
            .CombineLatest(b, (aItem, bItem) => (aItem % 9) + (bItem % 7))
            .Replay(1)
            .RefCount();
        _dummySub = _sums.Subscribe(_ => {});
    }

    public IObservable<int> getSums()
    {
        return _sums;
    }
}

简而言之,如果必须,请从主题开始。然后带走你的主题,并尽可能远离你的逻辑推动它们。

答案 1 :(得分:1)

所描述的案例就是所谓的'热'可观察 - 可观察到其生产者(排放源)在订阅之外创建, (正如Ben Lesh在Hot vs Cold Observables所述 - 建议阅读)。

正如Shlomo所说,受试者是Rx世界的“可变变量”,你可以通过使用Obsevable.create(听取事件并根据事件产生排放)来创造一个“冷”观察者,然后使用将其转换为ConnectableObservable(如share,publish)的运算符使其“热”,以便将其多播到多个不同时间订阅的观察者。

但是,在这种情况下,由于生产者是你的班级的本地人(事件是由这个班级生成的),所以可以使用主题来达到这个目的,因为你的班级本身就是排放源和产生事件的相互/状态变量。 (基于answer given by Erik Meijer和此blog post