将方法调用转换为可观察事件,好主意?

时间:2017-01-16 21:09:20

标签: system.reactive

我正在学习如何将IObservable合并到我的代码中。下面是一个简单类的两种不同方法,它打印出来自IObservable<string>的最新单词。哪种方法更清洁?我不喜欢WordPrinterWithCache,因为它引入了额外的状态(_lastWord)变量,而有趣的代码现在分散在整个班级中。我更喜欢WordPrinterWithExtraSubject因为有趣的代码本地化为构造函数。但大多数情况似乎更具功能性和反应性&#34 ;;我正在对两个&#34;事件&#34;的组合作出反应:(1)发出一个新单词,以及(2)调用PrintMostRecent方法。

但是我已经读到,如果不是绝对必要的话,使用主题是不可取的,我正在引入一个不必要的Subject<Unit>。基本上我在这里做的是从方法调用生成一个observable,所以我可以在更多的地方使用可观察的组合函数。我喜欢使用可观察的组合器和订阅来构建我的代码而不是使用&#34; old-style&#34;方法调用树。我不知道这是不是一个好主意。

public class WordPrinterWithCache
{
    string _lastWord = string.Empty;

    public WordPrinterWithCache(IObservable<string> words)
    {
        words.Subscribe(w => _lastWord = w);
    }

    public void PrintMostRecent() => Console.WriteLine(_lastWord);
}

public class WordPrinterWithExtraSubject
{
    Subject<Unit> _printRequest = new Subject<Unit>();

    public WordPrinterWithExtraSubject(IObservable<string> words)
    {
        _printRequest
            .WithLatestFrom(words.StartWith(string.Empty), (_, w) => w)
            .Subscribe(w => Console.WriteLine(w));
    }

    public void PrintMostRecent() => _printRequest.OnNext(Unit.Default);
}

我的代码中的实际情况是我有ICommand。当用户调用命令时(可能通过单击按钮),我想对特定可观察对象的最新值采取措施。例如,ICommand可能代表Delete,我想删除由IObservable<Guid>表示的列表中的所选项目。保持一堆&#34;最后一次发射的价值变得非常丑陋。每个observable的缓存变量。

我倾向于的方法是ICommand实现类似于您在下面看到的内容。这允许我编写像deleteCommand.WithLatestFrom(selectedItems,(d,s)=>s).Subscribe(selected=>delete(selected));

这样的代码
public class ObservableCommand : ICommand, IObservable<object>
{
    bool _mostRecentCanExecute = true;
    Subject<object> _executeRequested = new Subject<object>();

    public ObservableCommand(IObservable<bool> canExecute)
    {
        canExecute.Subscribe(c => _mostRecentCanExecute = c);
    }

    public event EventHandler CanExecuteChanged; // not implemented yet

    public bool CanExecute(object parameter) => _mostRecentCanExecute;

    public void Execute(object parameter) => _executeRequested.OnNext(parameter);

    public IDisposable Subscribe(IObserver<object> observer) => _executeRequested.Subscribe(observer);
}

2 个答案:

答案 0 :(得分:2)

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

这里的理论是IObservable<T>是一种回调机制。它允许某些东西回调到另一个它不知道的东西。但是在这种情况下,您可以了解IObservable<T>(参数)和另一个(WordPrinterWithCache)。 那么为什么会有这个额外的间接层呢?将值推送到IObservable<T>只能改为调用WordPrinterWithCache实例的方法。

在这种情况下,只需在另一件事上调用一个方法

public class WordPrinterWithCache
{
    private string _lastWord = string.Empty;

    public void SetLastWord(string word)
    {
        _lastWord = word;
    }

    public void PrintMostRecent() => Console.WriteLine(_lastWord);
}

现在这个课开始看起来毫无意义,但那可能没问题。简单就是好。

使用Rx帮助您进行分层。

上游图层依赖于下游图层。他们将直接在下游层调用方法(发出命令)。

下游层无法访问上游层。因此,要向它们公开数据,它们可以从方法返回值,也可以公开回调。 GoF Observer模式,.NET Events和Rx是向上游层提供回调的方法。

答案 1 :(得分:0)

虽然我不确定我是否会使用“将方法调用转换为可观察事件”这一术语,但我一直喜欢使用完全声明,函数和Rx驱动的代码。

您对ICommand实现的描述与我几年前写过的描述非常接近,并且已经使用了很多次并非常成功。此外,这已经成为我称之为“反应行为”的模式的基础,其提供了许多益处。来自我的博客:

  
      
  • 促进行为驱动的开发和单元测试。
  •   
  • 促进功能和线程安全的编程实践。
  •   
  • 降低(以及如果做得好,可以消除)副作用的风险,因为特定的行为是在一个名为方法的方法中被隔离的。
  •   
  • 停止'代码腐烂',因为所有行为都封装在特定命名的方法中。想要新的行为吗?添加新方法。不再需要特定的行为了吗?刚刚删除它。想要改变一个特定的行为?改变一种方法,并知道你没有破坏任何其他方法。
  •   
  • 提供用于聚合多个输入的简明机制,并将异步流程提升到一流状态。
  •   
  • 减少对实用程序类的需求,因为数据可以作为强类型的匿名类传递给管道。
  •   
  • 防止内存泄漏,因为所有行为都返回一次性处理,当处置时删除所有订阅并处置所有托管资源。
  •   

您可以阅读完整的文章on my blog