将IObservable传递给构造函数 - 好主意?

时间:2017-03-03 02:41:15

标签: system.reactive

我在这个网站上读到comment关于如何使用IObservable的说法..

作为一般规则(指南),我强烈建议不要将IObservable<T>作为方法的参数。显而易见的警告是,如果该方法是新的Rx运算符,例如SelectMySpecialBufferDebounce等。

因此,我一直在尝试将此建议应用于我的代码,并继续遇到违反此规则似乎很方便的情况。请查看下面的代码并比较PersonSelectorViewModelAPersonSelectorViewModelB。这些视图模型可能绑定到ComboBox的ItemsSource和SelectedValue属性。

我喜欢PersonSelectorViewModelA - 它在构造函数中使用IObservable - 更好,因为一旦构造它,它会自动响应添加到地址簿的人和从地址簿添加的人。它没有任何保姆照顾其业务和责任。 PersonSelectorViewModelB需要更多“维护和关注”,因为实例化它的代码需要记住定期调用UpdatePeople

那么哪种方法更好?

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Guid Id { get; set; }
}

public interface IAddressBookRepository
{
    void AddOrUpdatePerson(Person p);
    void DeletePerson(Guid id);
    IObservable<IEnumerable<Person>> People();
}

public class PersonSelectorViewModelA : BindableBase
{
    Guid? _selectedPersonId = null;
    ObservableCollection<Person> _peopleCollection = new ObservableCollection<Person>();
    IObservable<IEnumerable<Person>> _peopleSource;
    IDisposable _subscription;

    public PersonSelectorViewModelA(IObservable<IEnumerable<Person>> people)
    {
        _peopleSource = people;
    }

    public void OnNavigateTo()
    {
        _subscription =
            _peopleSource
            .Select(i => i.ToArray())
            .Subscribe(i =>
            {
                _peopleCollection.Clear();
                foreach (var p in i)
                {
                    _peopleCollection.Add(p);
                }
                if (!i.Any(j => j.Id == SelectedPersonId))
                {
                    SelectedPersonId = null;
                }
            });
    }

    public void OnNavigateAway() => _subscription?.Dispose();

    public Guid? SelectedPersonId {
        get { return _selectedPersonId; }
        set { SetProperty(ref _selectedPersonId, value); }
    }

    public ObservableCollection<Person> People => _peopleCollection;
}

public class PersonSelectorViewModelB : BindableBase
{
    Guid? _selectedPersonId = null;
    ObservableCollection<Person> _peopleCollection = new ObservableCollection<Person>();

    public void UpdatePeople(IEnumerable<Person> people)
    {
        _peopleCollection.Clear();
        foreach (var p in people)
        {
            _peopleCollection.Add(p);
        }
        if (!people.Any(j => j.Id == SelectedPersonId))
        {
            SelectedPersonId = null;
        }
    }

    public Guid? SelectedPersonId {
        get { return _selectedPersonId; }
        set { SetProperty(ref _selectedPersonId, value); }
    }

    public ObservableCollection<Person> People => _peopleCollection;
}

2 个答案:

答案 0 :(得分:1)

对于反应式ICommand,命令主机通常会知道何时启用和禁用该命令,并且可以手动(外部)而不是构造IObservable,然后将其传递给命令的构造函数。同样适用于显示FirstName和LastName串联的反应式FullName属性;使FullName成为IObserver更简单,而不是构建IObservable并将其传递给FullName属性的构造函数。因此,对于这些例子,我明白你的观点。

我的数据层包含IObservable<Person[]> GetPeople(string city)IObservable<Person> GetPerson(Guid id)等功能,因此视图可以显示最新信息。这一层并不知道它的消费者是谁;有许多需要此数据的视图模型。假设一个消费者是一个人物选择者&#34;视图模型由另一个视图模型X托管。而不是让X订阅可观察对象并从外部驱动&#34;人员选择器&#34;它所拥有的,我给了人们选择&#34;在它的构造函数中的observable,所以X可以更加放手。 X并不关心人们选择器如何完成它的工作 - 它只是希望它能够做到这一点。 X&#34;了解&#34;可观察的,但只需要从一些数据层中获取它并使用它来构建人员选择器。

我认为这类似于WPF ListView。您可以将ListView的ItemsSource属性设置为支持INotifyCollectionChanged的集合,然后ListView订阅事件并自动更新其内容。这个数据绑定比拥有ListView订阅列表更改的视图模型更简单,并在ListView上调用一些UpdateItems方法。

听起来你几乎从不将一个observable传递给一个对象的构造函数。因此,在构造函数依赖注入方面,一个类不应该依赖于可观察的序列。但这可能是正确的,可能不是你所说的。也许我没有在原始问题中正确解释这个场景。否则,一个类只能依赖于它创建的可观察对象(奇怪的)或它通过对其依赖项的方法调用所捕获的可观察对象(类似于构造函数注入)。

答案 1 :(得分:0)

我的标准建议仍然有效。 你的代码有效,所以这是我的看法。 我相信让计算机做你想做的事情并不是很难,但让另一个人明白你想让计算机去做的事情要困难得多。

我认为您要么选择B(从外部更新的虚拟VM),要么传入IAddressBookRepository。 但是传入IObservable<T> IMO只是很奇怪。 如果我们认为IObservable<T>只是Rx实现的回调/观察者模式。 接下来我们知道观察者模式是为了我们想要一个我们依赖的系统,给我们回电话,而不是明确地了解我们。 但是,如果我们传入IObservable<T>,那么传递它的东西清楚地知道IObservable<T>然后它正在创建的东西。 那么在这里购买我们的回调机制是什么期望双向间接?