【问题标题】:Is it ok to pass a subject into a component将主题传递给组件是否可以
【发布时间】:2011-04-13 13:15:57
【问题描述】:

我想知道从架构的角度来看是否可以将主题传递给组件。我真正想要的是让组件公开一个可观察的。但是,我想控制这个可观察流的来源,这就是为什么我问是否可以传入组件可以引发“事件”的主题。

好的,让我们详细说明一下。

假设,我们正在设计一个组件,它接受用户输入、限制击键并显示结果列表。实际搜索发生在另一个服务组件上。

我想这样设计 SearchWidget 创建器功能:

//notice how I just avoided the word "constructor". However, conside this code as
//language agnostic. Could be RxJs or Rx .NET. It's Rx(ish)!

function SearchWidget(userInputStream, resultStream){
    // do some funky Rx hotness! 

}

更高级别的组件(例如控制器/调解器)实际上会连接流。

显然,resultStream 需要 inputStream 来完成工作。

在上面的示例中,从 SearchWidget 的角度来看,resultStream 将是一个简单的可观察对象,它可以在其上侦听结果列表。但是,它将在更高级别的组件中作为 Subject 实现。

相比之下,从 SearchWidget 的角度来看,userInputStream 将是一个主题,但它将在更高的组件级别上被实例化,因为我们需要提前获得它来连接 resultStream。不过,从更高级别组件的角度来看,它是一个简单的可观察对象。

高阶代码可能如下所示:

//This code lives in a higher order component (say a controller/mediator)
var resultStream = new Rx.Subject();
var userInputStream = new Rx.Subject();
userInputStream
    .Throttle(500)
    .DistinctUntilChanged()
    .Select(service.search)  //service comes from somewhere.
    .Switch()
    .Subscribe(resultStream.OnNext,
               resultStream.OnError,
               resultStream.OnCompleted);


var searchWidget = new SearchWidget(userInputStream, resultStream.AsObservable());

在上面的实现中,我在 SearchWidget 被初始化之前使用了 userInputStream。当然我也可以这样实现:

//implementation of the search widget
function SearchWidget(resultStream){        
    var userInputStream = new Rx.Subject();
    // provide something like getUserInputStream() 
    // that will return unserInputStream.AsObservable()

    // do some funky Rx hotness! 
}

//This code lives in a higher order component (say a controller/mediator)
var resultStream = new Rx.Subject();
var searchWidget = new SearchWidget(resultStream);

//we need to initialize the searchWidget in advance to get the userInputStream

searchWidget
    .getUserInputStream() 
    .Throttle(500)
    .DistinctUntilChanged()
    .Select(service.search)  //service comes from somewhere.
    .Switch()
    .Subscribe(resultStream.OnNext,
               resultStream.OnError,
               resultStream.OnCompleted);

因此,从封装的角度来看,第二种实现可能看起来更健壮。但是,传递主题提供了更丰富的灵活性。

由于使用事件流的概念非常现代,因此在设计使用事件流的应用程序时,我一直在努力寻找更全面的最佳实践。

【问题讨论】:

    标签: system.reactive reactive-programming


    【解决方案1】:

    对我来说,它似乎只需要重新洗牌,并思考一下什么是最清晰的方式来表达正在发生的事情(Rx 很容易被过度使用)。

    查看您的示例,我认为将用户输入公开为 IObservable(通过您的 GetUserInputStream)并没有错,继续在更高级别的控制器/中介中处理流也没有错,但对我来说没有需要传入 resultStream 主题。我看不出有任何理由不能在 SearchWidget 上使用普通方法来处理结果。比如:

    var searchWidget = new SearchWidget();
    
    searchWidget
        .GetUserInputStream() 
        .Throttle(500)
        .DistinctUntilChanged()
        .Select(service.search)  //service comes from somewhere.
        .Switch()
        .Subscribe(result => searchWidget.HandleSearchResult(result),
                   ex => searchWidget.HandleSeachError(ex),
                   () => searchWidget.HandleSearchComplete());
    

    它更加明确,您将能够通过命名良好的方法以更清晰的方式表达 SearchWidget 上的结果。

    如果您确实想通过 Rx 处理这些响应的内部,那么在内部实例化主题并处理来自方法调用的响应不会有任何问题。如:

    public class SearchWidget
    {
       private ISubject subject;
    
       public SearchWidget()
       {
          this.subject = new Subject();
    
          //do funky rx stuff on the subject here
       }
    
       public void HandleSearchResult(SearchResult result)
       { 
          subject.OnNext(result);
       }
    
       public void HandleSearchError(Exception ex)
       {
          subject.OnError(ex);
       } 
    
       public void HandleSearchComplete()
       {
          subject.OnCompleted();
       }
    
       public IObservable<MouseEvent> GetUserInputStream()
       {
          return someUserInputStream; // whatever your stream is
       }
    
    }
    

    【讨论】:

    • 感谢您的回答。但是,我不明白。您的代码中的“someUserInputStream”是什么?你真的指的是“主题”吗?如果是这样,您正在从外部发出通知,通知似乎实际上打算注入结果。另请注意,在您的实现中,我仍然需要提前初始化小部件。这就是我想避免的。然而,更大的图景更加复杂,我只是简单地举个例子。不管怎样,我从讨论中学到了一些东西。我认为总的来说这个主题应该是内部的,因为我们想避免(1/2)
    • (2/2) 其他人在他不应该拥有的流上发出通知。然而,这不是我的目标。我的目标是能够在初始化另一个组件之前将流连接在一起,我想我会走这条路,看看它是如何进行的......
    • @Christoph - someUserInputStream 代表您的 getUserInputStream() 通常返回的任何内容。它只是意味着您当前使用的任何东西。真的可以是任何事件流。
    • 那么主题是什么,为什么通过这些方法从外部提出。如果您在 SearchWidget 上提供一个 HandleSearchResult 方法来获取传递的结果,则该小部件可以直接显示结果(例如,绘制一些文本)。我想我们可能对场景有不同的理解。我认为 SearchWidget 是一个带有文本框和绘制结果区域的小部件(例如:jqueryui.com/demos/autocomplete/#default
    【解决方案2】:

    由于使用事件流的概念非常现代,因此在设计使用事件流的应用程序时,我一直在努力寻找更全面的最佳实践。

    当然,在前沿很酷,但这也意味着你必须在前进的过程中发现“正确的方式”!

    所以,请记住 ISubject 既是 IObserver(发布部分),也是 IObservable(订阅部分) - 如果您的意图是您的方法的客户端应该是订阅者,则只给他们 IObservable 部分(即,将主题转换为IObservable),如果你的方法的客户端应该是发布者,给他们 IObserver 部分。

    当你使用 Observable.CreateWithDisposable() 时你会看到同样的东西,你得到的是 IObserver 位(因为你正在创建一个 IObservable,你的工作就是发布东西!)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-22
      • 2018-04-17
      • 2019-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-12
      相关资源
      最近更新 更多