Можно ли передать предмет в компонент? - PullRequest
1 голос
/ 13 апреля 2011

Интересно, можно ли с архитектурной точки зрения передавать объект в компонент? То, что я на самом деле хочу, это чтобы компонент выставлял наблюдаемое. Однако я хотел бы проконтролировать, откуда исходит этот наблюдаемый поток, поэтому я спрашиваю, можно ли передать объект, где компонент может вызывать «события».

Хорошо, давайте уточним это.

Скажем, мы разрабатываем компонент, который принимает пользовательский ввод, регулирует нажатия клавиш и отображает список результатов. Фактический поиск происходит в другом сервисном компоненте.

Я бы хотел создать функцию создателя 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, на котором он может прослушивать список результатов. Однако он будет реализован как субъект в компоненте более высокого уровня.

Напротив, userInputStream будет субъектом с точки зрения SearchWidget, но он будет создан на более высоком уровне компонентов, потому что он нам нужен заранее, чтобы подключить 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());

В приведенной выше реализации я использую userInputStream до инициализации SearchWidget. Конечно, я мог бы также реализовать это так:

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

Таким образом, с точки зрения инкапсуляции вторая реализация может выглядеть более надежной. Тем не менее, переход к теме обеспечивает большую гибкость.

Поскольку концепция работы с потоками событий довольно современна, я изо всех сил пытаюсь найти лучшие практики для более широкой картины при разработке приложений с потоками событий.

Ответы [ 2 ]

2 голосов
/ 14 апреля 2011

Для меня это выглядит так, как будто нужно немного перестановить и немного подумать о том, что было бы самым ясным способом выразить происходящее (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
   }

}
1 голос
/ 14 апреля 2011

Поскольку концепция работы с потоками событий довольно современна, я изо всех сил пытаюсь найти лучшие практики для более широкой картины при разработке приложений с потоками событий.

Определенно, крутобыть на границе, но это также означает, что вы должны открывать «правильный путь» по ходу дела!

Итак, помните, что ISubject является одновременно IObserver (часть публикации) и IObservable (подписка)часть) - если ваше намерение состоит в том, чтобы клиенты вашего метода были подписчиками, предоставьте им только часть IObservable (т. е. приведите Subject к IObservable), а если клиенты вашего метода должны быть издателями, предоставьте им часть IObserver.

То же самое вы видите, когда используете Observable.CreateWithDisposable (), где вам дается бит IObserver (поскольку вы создаете IObservable, ваша задача - публиковать вещи!)

...