问题描述
我正在学习如何将 IObservable
合并到我的代码中.下面是用于从 IObservable
打印出最新单词的简单类的两种不同方法.哪种方法更清洁?我不喜欢 WordPrinterWithCache
,因为它引入了额外的状态(_lastWord
)变量,并且有趣的代码现在分散在整个类中.我更喜欢 WordPrinterWithExtraSubject
因为有趣的代码本地化到构造函数.但大多数情况下,它似乎更具有功能性和反应性";我正在对两个事件"的组合做出反应:(1) 发出一个新词,以及 (2) 调用 PrintMostRecent
方法.
I'm learning how to incorporate IObservable
into my code. Below are two different approaches for a simple class that prints out the most recent word from an IObservable<string>
. Which is the cleaner approach? I don't like WordPrinterWithCache
because it introduces additional state (the _lastWord
) variable and the interesting code is now scattered throughout the class. I prefer WordPrinterWithExtraSubject
because the interesting code is localized to the constructor. But mostly it seems more consistently functional and "reactive"; I am reacting to the combination of two "events": (1) a new word being emitted, and (2) the invocation of the PrintMostRecent
method.
然而,我已经读到,在不是绝对必要的情况下使用 Subjects 是不可取的,并且我引入了一个不必要的 Subject
.基本上我在这里做的是从方法调用生成一个可观察的,这样我就可以在更多的地方使用可观察的组合器函数.我喜欢使用可观察组合器和订阅来构建我的代码的想法,而不是使用旧式"方法调用树.我不知道这是不是个好主意.
However I have read that using Subjects is not desirable when it is not strictly necessary and I am introducing an unnecessary Subject<Unit>
. Essentially what I'm doing here is generating an observable from a method call so I can use the observable combinator functions in more places. I like the idea of building up my code using the observable combinators and subscriptions rather than using "old-style" method invocation trees. I don't know if this is a good idea.
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
.当用户调用命令(可能通过单击按钮)时,我想对特定 observable 的最新值采取行动.例如,也许 ICommand
代表 Delete
,我想删除由 IObservable
表示的列表中的选定项目.为每个 observable 保留一堆最后发出的值"缓存变量会变得很丑陋.
The actual scenario in my code is that I have an ICommand
. When the user invokes the command (maybe by clicking a button), I want to take action on the most recent value of a specific observable. For example, maybe the ICommand
represents Delete
and I want to delete the selected item in a list that is represented by an IObservable<Guid>
. It gets ugly to keep a bunch of "last emitted value" cache variables for each observable.
我倾向于采用一种类似于您在下面看到的 ICommand
实现的方法.这允许我编写像 deleteCommand.WithLatestFrom(selectedItems,(d,s)=>s).Subscribe(selected=>delete(selected));
The approach I'm leaning toward is an ICommand
implementation kind of like what you see below. This allows me to write code like 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);
}
推荐答案
虽然我不确定我是否会使用术语将方法调用转换为可观察事件",但我一直喜欢使用完全声明式,函数式和 Rx 驱动的代码已经有一段时间了.
While I'm not sure I'd use the term "turning a method call into an observable event", I have been a fan of using fully declarative, functional and Rx driven code for a while now.
您对 ICommand 实现的描述与我几年前写的非常接近,并且已经使用了很多次并且非常成功.此外,这已经成为我称之为反应性行为"的模式的基础,它提供了许多好处.来自我的博客:
Your description of an ICommand implementation matches very closely with one I wrote a couple of years ago and have used many times and very successfully since. Furthermore, this has become the basis for a pattern I refer to as "Reactive Behaviors" which provides numerous benefits. From my blog:
- 促进行为驱动的开发和单元测试.
- 促进函数式和线程安全编程实践.
- 降低(如果做得好,可以消除)副作用的风险,因为特定行为被隔离在一个命名良好的方法中.
- 停止代码腐烂",因为所有行为都封装在特定命名的方法中.想要新行为?添加新方法.不再想要特定的行为?刚刚删除了它.想要改变特定的行为吗?更改一种方法并知道您没有破坏任何其他方法.
- 为聚合多个输入提供简洁的机制,并将异步流程提升到一流状态.
- 减少对实用程序类的需求,因为数据可以作为强类型匿名类通过管道传递.
- 防止内存泄漏,因为所有行为都返回一个一次性对象,当它被释放时会删除所有订阅并释放所有托管资源.
您可以在我的博客上阅读全文.
这篇关于把一个方法调用变成一个可观察的事件,好主意吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!