可以将主题传递给组件吗

发布于 2024-11-01 01:18:38 字数 2314 浏览 4 评论 0原文

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

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

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

我想设计这样的 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 来完成工作。

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

相反,从 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);

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

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

I wonder if its ok from an architectural standpoint to pass a subject into a component. What I actually want is to have the component expose an observable. However, I would like to control where this observable stream comes from so thats why Im asking if its fine to pass in a subject where the component can raise "events" on.

Ok, let's elaborate on this.

Say, we are designing a component that takes userinput, throttles the keystrokes and shows up a result list. The actual search is happening on another service component.

I would like to design the SearchWidget creator function like this:

//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! 

}

A higher level component (say a controller/mediator) will actually hook up the streams.

Obviously, the resultStream needs the inputStream to get the work done.

In our example above, the resultStream will be a simply observable from the perspective of the SearchWidget on which it can listen for the result list. However it will be implemented as Subject within the higher level component.

In contrast, the userInputStream will be a subject from the perspective of the SearchWidget but it will be instanced on the higher component level because we need it in advance to get the resultStream hooked up. Though, from the perspective of the higher level component, its a simple observable.

The higher order code may look like this:

//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());

In the implementation above, I use the userInputStream before the SearchWidget was initialized. Of course I could also implement it this way:

//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);

So, from the standpoint of encapsulation, the second implementation may look more robust. However, passing in the subject provides richer flexibility.

Since the concept of working with event streams is pretty modern I'm struggeling to find best practices for the bigger picture when designing applications with event streams.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

懵少女 2024-11-08 01:18:38

对我来说,它看起来只需要进行一些重新洗牌,并思考什么是表达正在发生的事情的最清晰的方式(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
   }

}

For me it looks like it just needs a bit of a reshuffle and a bit of thought into what would be the clearest way to express what is going on (Rx can very easily be overused).

Looking at your example I see nothing wrong with exposing the user input as an IObservable (through your GetUserInputStream), and also nothing wrong in continuing to handle the stream in your higher level controllers/mediators, but to me there isn't a need to pass the resultStream Subject in. I can't see any reason you cant use normal methods on the SearchWidget to handle the results. Something like:

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());

It's a lot more explicit and you'll be able to express in a much clearer way, through well named methods, what the results will be on the SearchWidget.

If you did then want to handle the internals to these responses through Rx, there would be nothing wrong with instancing a subject internally and processing the responses from the method calls. Such as:

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
   }

}
灯角 2024-11-08 01:18:38

由于使用事件流的概念非常现代,因此在设计具有事件流的应用程序时,我正在努力寻找更宏观的最佳实践。

当然,身处前沿是很酷的事情,但这也意味着你必须在前进的过程中发现“正确的道路”!

因此,请记住 ISubject 既是 IObserver(发布部分),又是 IObservable(订阅部分) - 如果您的意图是您的方法的客户端应该是订阅者,则只给它们 IObservable 部分(即,将Subject 转换为 IObservable),如果您的方法的客户端应该是发布者,请为他们提供 IObserver 部分。

当您使用 Observable.CreateWithDisposable() 时,您会看到同样的事情,其中​​您得到的东西是 IObserver 位(因为您正在创建 IObservable,所以您的工作就是发布内容!)

Since the concept of working with event streams is pretty modern I'm struggeling to find best practices for the bigger picture when designing applications with event streams.

Definitely, it's cool to be on the frontier, but it also means you have to discover "the right way" as you go along!

So, remember that ISubject is both an IObserver (the Publish part), and an IObservable (the Subscribe part) - if your intent is that your method's clients should be Subscribers, only give them the IObservable part (i.e. cast Subject to IObservable), and if your method's clients should be Publishers, give them the IObserver part.

You see the same thing when you use Observable.CreateWithDisposable(), where the thing you're given is the IObserver bit (since you're creating an IObservable, your job is to publish stuff!)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文