SlideShare a Scribd company logo
Почему	
  Reac,ve	
  Extensions?	
  
•  Отзывчивость	
  приложений	
  
•  Упрощение	
  кода	
  работы	
  со	
  временем	
  
•  Упрощение	
  управления	
  потоками	
  
•  Упрощение	
  подписки-­‐отписки	
  для	
  событий	
  
•  Тестируемость	
  кода	
  
•  LINQ	
  для	
  событий	
  и	
  потоков	
  данных	
  
•  Наличие	
  реализаций	
  для	
  других	
  языков	
  
программирования	
  
2	
  
IEnumerable<T>	
  vs	
  IObservable<T>	
  
3	
  
Преимущества	
  Rx	
  перед	
  событиями	
  
•  События	
  однопоточны.	
  Все	
  обработчики	
  будут	
  вызываться	
  по	
  очереди	
  
в	
  потоке	
  который	
  запустил	
  событие.	
  
•  Если	
  в	
  одном	
  из	
  обработчиков	
  события	
  будет	
  выброшено	
  исключение	
  
все	
  остальные	
  обработчики	
  не	
  будут	
  вызваны	
  
•  Все	
  подписки	
  на	
  события	
  необходимо	
  отписывать.	
  Для	
  этого	
  придется	
  
хранить	
  ссылку	
  на	
  метод	
  для	
  отписки	
  и	
  объект	
  
•  Rx	
  поддерживает	
  async/await	
  для	
  запросов	
  требующих	
  ожидания	
  
конкретного	
  элемента	
  
•  Rx	
  предоставляет	
  методы	
  для	
  управления	
  временем	
  и	
  потоками,	
  а	
  
также	
  тестовые	
  реализации	
  управляющих	
  объектов.	
  	
  
•  Rx	
  предоставляет	
  методы	
  для	
  связи	
  нескольких	
  потоков	
  данных	
  в	
  
один	
  
•  Rx	
  предоставляет	
  возможность	
  обрабатывать	
  данные	
  аналогично	
  
LINQ-­‐запросу	
  в	
  функциональном	
  стиле.	
  
4	
  
Основные	
  интерфейсы	
  Rx	
  
•  IObservable<T>	
  
	
  	
  	
  IDisposable	
  Subscribe(IObserver<T>	
  observer);	
  
•  IObserver<T>	
  
	
  void	
  OnNext(T	
  value);	
  
	
  void	
  OnError(Exception	
  error);	
  
	
  void	
  OnCompleted();	
  
•  IScheduler	
  
•  ISubject<T>	
  :	
  IObservable<T>, IObserver<T>
5	
  
Простой	
  класс	
  с	
  Rx	
  
	
  	
  	
  	
  public	
  class	
  LogoutManager	
  
	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  public	
  IObservable<Unit>	
  Logout	
  {	
  get;	
  private	
  set;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  public	
  IObserver<Unit>	
  UserActionsObserver	
  {	
  get;	
  private	
  set;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  public	
  IObserver<Unit>	
  LogoutCommandsObserver	
  {	
  get;	
  private	
  set;	
  }	
  	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  public	
  LogoutManager(TimeSpan	
  timeout)	
  
	
  	
  	
  	
  	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  userActionsSubject	
  =	
  new	
  Subject<Unit>();	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  logoutSubject	
  =	
  new	
  Subject<Unit>();	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  UserActionsObserver	
  =	
  userActionsSubject.AsObserver();	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  LogoutCommandsObserver	
  =	
  logoutSubject.AsObserver();	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Logout	
  =	
  userActionsSubject	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .StartWith(Unit.Default)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Throttle(timeout)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Merge(logoutSubject);	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
6	
  
Потоки	
  данных	
  в	
  LogoutManager	
  
	
  Logout	
  =	
  userActionsSubject	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .StartWith(Unit.Default)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Throttle(timeout)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Merge(logoutSubject);	
  
7	
  
Интеграция	
  Rx	
  с	
  существующими	
  
событиями	
  
•  Короткая:	
  
Observable.FromEventPattern(source,	
  "PropertyChanged");	
  
•  Типобезопасная:	
  
Observable.FromEventPattern	
  
<PropertyChangedEventHandler,PropertyChangedEventArgs>(
	
  	
  	
  	
  	
  	
  	
  handler	
  =>	
  source.PropertyChanged	
  +=	
  handler,	
  
	
  	
  	
  	
  	
  	
  	
  handler	
  =>	
  source.PropertyChanged	
  -­‐=	
  handler)	
  
•  Промежуточные	
  варианты	
  с	
  указанием	
  типов	
  только	
  
для	
  части	
  операндов	
  
	
  
	
   8	
  
Текстовый	
  фильтр	
  на	
  Rx	
  
9	
  
Текстовый	
  фильтр	
  на	
  Rx	
  
	
  Observable.FromEventPattern	
  
<TextChangedEventHandler,	
  TextChangedEventArgs>(
	
  	
  handler	
  =>	
  filterTextBox.TextChanged	
  +=	
  handler,
	
  	
  handler	
  =>	
  filterTextBox.TextChanged	
  -­‐=	
  handler)
	
  	
  	
  	
  	
  	
  .Select(x	
  =>	
  x.Sender)
	
  	
  	
  	
  	
  	
  .Cast<TextBox>()
	
  	
  	
  	
  	
  	
  .Select(x	
  =>	
  x.Text)
	
  	
  	
  	
  	
  	
  .Where(x	
  =>	
  x.Length	
  >	
  2)
	
  	
  	
  .Subscribe(FilterCollection);	
  
10	
  
Расширенный	
  фильтр	
  на	
  Rx	
  
•  Исходная	
  коллекция	
  также	
  может	
  меняться	
  
•  Пока	
  пользователь	
  продолжает	
  ввод	
  не	
  имеет	
  смысла	
  
перезапускать	
  поиск	
  
•  Логика	
  фильтрации	
  может	
  быть	
  достаточно	
  сложна	
  и	
  
отнимать	
  много	
  времени	
  
•  Пользователь	
  должен	
  видеть	
  результаты	
  по	
  мере	
  их	
  
поступления,	
  а	
  не	
  после	
  завершения	
  фильтрации	
  
•  Пользователь	
  может	
  начать	
  вводить	
  фильтр,	
  а	
  затем	
  
вернуть	
  старый.	
  Повторную	
  фильтрацию	
  при	
  этом	
  не	
  
надо	
  запускать	
  
11	
  
Расширенный	
  фильтр	
  на	
  Rx	
  
	
  public	
  interface	
  IReactiveCollection<TItem>	
  :	
  	
   	
  
	
   	
   	
  INotifyPropertyChanged,	
  IDisposable
{
	
  	
  //	
  Источник	
  данных	
  (полная	
  коллекция)	
  
	
  	
  	
  	
  	
  	
  	
  	
  TItem[]	
  Source	
  {	
  get;	
  set;	
  }	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  //	
  Отображаемая	
  часть	
  данных	
  (отфильтрованная	
  коллекция)	
  
	
  	
  	
  	
  	
  	
  	
  	
  TItem[]	
  View	
  {	
  get;	
  }	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  //	
  Строка	
  фильтра	
  
	
  	
  	
  	
  	
  	
  	
  	
  string	
  Filter	
  {	
  get;	
  set;	
  }	
  	
  	
  	
  
	
  	
  	
  }	
  
12	
  
Расширенный	
  фильтр	
  на	
  Rx	
  
public	
  ReactiveCollection(Func<TItem,	
  string,	
  bool>	
  filterFunc)	
  {
	
  	
  	
  	
  	
  var	
  buffer	
  =	
  new	
  ObservableCollection<TItem>();	
  
	
  	
  	
  	
  	
  _subscription	
  =	
  this.PropertyChangedAsObservable(x	
  =>	
  x.Filter)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Throttle(TimeSpan.FromMilliseconds(200))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Where(_	
  =>	
  string.IsNullOrWhiteSpace(Filter)	
  ||	
  Filter.Length	
  >	
  2)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .DistinctUntilChanged(_	
  =>	
  Filter)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Merge(this.PropertyChangedAsObservable(x	
  =>	
  x.Source))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Do(_	
  =>	
  buffer.Clear())
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Select(_	
  =>	
  Source.EmptyIfNull()
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .ToObservable()
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Where(x	
  =>	
  filterFunc(x,	
  Filter))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Buffer(TimeSpan.FromMilliseconds(200))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Do(x	
  =>	
  x.ForEach(buffer.Add)))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Switch()
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Subscribe(_	
  =>	
  View	
  =	
  buffer.ToArray());	
  
}
13	
  
Потоки	
  данных	
  в	
  расширенном	
  
фильтре	
  
this.PropertyChangedAsObservable(x	
  =>	
  x.Filter)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Throttle(TimeSpan.FromMilliseconds(200))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Where(_	
  =>	
  string.IsNullOrWhiteSpace(Filter)	
  ||	
  Filter.Length	
  >	
  2)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .DistinctUntilChanged(_	
  =>	
  Filter)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Merge(this.PropertyChangedAsObservable(x	
  =>	
  x.Source))
14	
  
Потоки	
  данных	
  в	
  расширенном	
  
фильтре	
  
	
   	
  .Select(_	
  =>	
  Source.EmptyIfNull()
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .ToObservable()
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Where(x	
  =>	
  filterFunc(x,	
  Filter))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Buffer(TimeSpan.FromMilliseconds(200))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Do(x	
  =>	
  x.ForEach(buffer.Add)))
	
  	
  	
  	
  	
  	
  	
  	
  	
  .Switch()	
  
15	
  
Управление	
  потоками	
  в	
  Rx	
  
public	
  ReactiveCollection(Func<TItem,	
  string,	
  bool>	
  filterFunc,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  )	
  {
	
  	
  	
  	
  	
  var	
  buffer	
  =	
  new	
  ObservableCollection<TItem>();	
  
	
  	
  	
  	
  	
  _subscription	
  =	
  this.PropertyChangedAsObservable(x	
  =>	
  x.Filter)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Throttle(TimeSpan.FromMilliseconds(timeout))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Where(_	
  =>	
  string.IsNullOrWhiteSpace(Filter)	
  ||	
  Filter.Length	
  >	
  3)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .DistinctUntilChanged(_	
  =>	
  Filter)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Merge(this.PropertyChangedAsObservable(x	
  =>	
  x.Source))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Do(_	
  =>	
  buffer.Clear())
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Select(_	
  =>	
  Source.EmptyIfNull()
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .ToObservable(	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  )
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Where(x	
  =>	
  filterFunc(x,	
  Filter))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Buffer(TimeSpan.FromMilliseconds(timeout))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Do(x	
  =>	
  x.ForEach(buffer.Add)))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Switch()	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .ObserveOn(observeOn)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Subscribe(_	
  =>	
  View	
  =	
  buffer.ToArray());	
  
}
IScheduler	
  filterScheduler,	
  IScheduler	
  observeOn	
  	
  
filterScheduler	
  
16	
  
Применение	
  фильтра	
  на	
  Rx	
  
17	
  
Проблемы	
  реализации	
  при	
  помощи	
  
событий	
  
•  Необходимость	
  реализации	
  задержки	
  для	
  
событий	
  –	
  для	
  того,	
  чтобы	
  не	
  загружать	
  списки	
  
клиентов	
  и	
  счетов	
  много	
  раз	
  
•  Постоянные	
  подписки	
  и	
  отписки	
  на	
  события	
  
элементов	
  коллекции	
  клиентов,	
  отсутствие	
  
отписок	
  быстро	
  приведет	
  к	
  утечке	
  памяти	
  в	
  
приложении.	
  
•  Код	
  для	
  управления	
  потоками	
  –	
  фильтрация	
  в	
  
отдельном	
  потоке,	
  вывод	
  результатов	
  в	
  другом	
  
18	
  
Применение	
  фильтра	
  на	
  Rx	
  
Companies	
  =	
  new	
  ReactiveCollection<Company>((x,	
  y)	
  =>	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  string.IsNullOrWhiteSpace(y)	
  ||	
  x.Name.Contains(y))
	
  {	
  	
  	
  	
  	
  Source	
  =	
  CompanyList	
  	
  	
  	
  };	
  
	
  
	
  Clients	
  =	
  new	
  ReactiveCollection<Client>((x,	
  y)	
  =>	
   	
  	
  	
   	
  
	
  	
  	
  	
  	
  string.IsNullOrWhiteSpace(y)	
  ||	
  x.Name.Contains(y));	
  
	
  Accounts	
  =	
  new	
  ReactiveCollection<Account>(SlowFilter);	
  
	
  private	
  bool	
  SlowFilter(Account	
  x,	
  string	
  y)
	
  {
	
  	
  Thread.Sleep(random.Next(50,	
  250));
	
  	
  return	
  string.IsNullOrWhiteSpace(y)	
  ||	
  x.Number.ToString().Contains(y);
	
  }
	
  
19	
  
Применение	
  фильтра	
  на	
  Rx	
  
	
  Companies.Source.Select(x	
  =>	
  x.PropertyChangedAsObservable(y	
  =>	
  y.Selected))
	
  	
  	
  	
  	
  	
  .Merge()
	
  	
  	
  	
  	
  	
  .Throttle(TimeSpan.FromMilliseconds(200))
	
  	
  	
  	
  	
  	
  .ObserveOnDispatcher()
	
  	
  	
  	
  	
  	
  .Subscribe(_	
  =>	
  Clients.Source	
  =	
  GetClients(Companies.Source
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Where(x	
  =>	
  x.Selected)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Select(x	
  =>	
  x.Id)));	
  
	
  
	
  Clients.PropertyChangedAsObservable(x	
  =>	
  x.Source)
	
  	
  	
  	
  .Select(_	
  =>	
  Clients.Source)
	
  	
  	
  	
  .Select(x	
  =>	
  x.Select(y	
  =>	
  y.PropertyChangedAsObservable(z	
  =>	
  z.Selected))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Merge()
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Throttle(TimeSpan.FromMilliseconds(100)))
	
  	
  	
  	
  .Switch()
	
  	
  	
  	
  .ObserveOnDispatcher()
	
  	
  	
  	
  .Subscribe(_	
  =>	
  Accounts.Source	
  =	
  GetAccountSource(Clients.Source 	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  .Where(x	
  =>	
  x.Selected)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Select(x	
  =>	
  x.Id)));	
  
20	
  
А	
  что	
  с	
  тестированием?	
  
•  Управление	
  временем	
  
•  Управление	
  потоками	
  
•  Буферизация	
  
	
  
•  Скорость	
  выполнения	
  тестов	
  
21	
  
Тестирование	
  Rx	
  при	
  помощи	
  
интерфейса	
  IScheduler	
  
	
  	
  	
  	
  public	
  class	
  LogoutManager	
  
	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  public	
  IObservable<Unit>	
  Logout	
  {	
  get;	
  private	
  set;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  public	
  IObserver<Unit>	
  UserActionsObserver	
  {	
  get;	
  private	
  set;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  public	
  IObserver<Unit>	
  LogoutCommandsObserver	
  {	
  get;	
  private	
  set;	
  }	
  	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  public	
  LogoutManager(TimeSpan	
  timeout,	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  )	
  
	
  	
  	
  	
  	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  userActionsSubject	
  =	
  new	
  Subject<Unit>();	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  logoutSubject	
  =	
  new	
  Subject<Unit>();	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  UserActionsObserver	
  =	
  userActionsSubject.AsObserver();	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  LogoutCommandsObserver	
  =	
  logoutSubject.AsObserver();	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Logout	
  =	
  userActionsSubject.StartWith(Unit.Default)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Throttle(timeout,	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  )	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Merge(logoutSubject);	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  public	
  LogoutManager(TimeSpan	
  timeout)	
  :	
  this(timeout,	
  Scheduler.Default)	
  	
  {	
  }	
  
	
  	
  	
  	
  }	
  
	
  IScheduler	
  scheduler	
  
scheduler	
  
22	
  
Тестирование	
  Rx	
  
[Test]	
  	
  
public	
  void	
  OnInactivity_Logout()	
  	
  
{
	
  	
  	
  	
  	
  var	
  testScheduler	
  =	
  new	
  TestScheduler();
	
  	
  	
  	
  	
  var	
  manager	
  =	
  new	
  LogoutManager(TimeSpan.FromMinutes(5),	
  testScheduler);
	
  	
  	
  	
  	
  manager.Logout.Subscribe(_	
  =>	
  Assert.Pass());
	
  	
  	
  	
  	
  testScheduler.AdvanceBy(TimeSpan.FromMinutes(6).Ticks);
	
  	
  	
  	
  	
  Assert.Fail();	
  	
  	
  	
  	
  	
  	
  	
  	
  
}	
  
23	
  
Тестирование	
  Rx	
  
[Test]	
  	
  
public	
  void	
  OnActivityWithinThreshold_DoNotLogout()	
  	
  
{
	
  	
  	
  	
  	
  var	
  testScheduler	
  =	
  new	
  TestScheduler();
	
  	
  	
  	
  	
  var	
  manager	
  =	
  new	
  LogoutManager(TimeSpan.FromMinutes(5),	
  testScheduler);
	
  	
  	
  	
  	
  manager.Logout.Subscribe(_	
  =>	
  Assert.Fail());
	
  	
  	
  	
  	
  for	
  (var	
  i	
  =	
  0;	
  i	
  <	
  10;	
  i++)	
  	
  
	
  	
  	
  	
  	
  {
manager.UserActionsObserver.OnNext(Unit.Default);
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  testScheduler.AdvanceBy(TimeSpan.FromMinutes(3).Ticks);
}
}	
  
24	
  
Тестирование	
  Rx	
  
[Test]	
  	
  
public	
  void	
  OnActivityCommand_LogoutImmediately()	
  	
  	
  
{
	
  	
  	
  	
  	
  var	
  manager	
  =	
  new	
  LogoutManager(TimeSpan.FromMinutes(5));
	
  	
  	
  	
  	
  manager.Logout.Subscribe(_	
  =>	
  Assert.Pass());
	
  	
  	
  	
  	
  manager.LogoutCommandsObserver.OnNext(Unit.Default);
	
  	
  	
  	
  	
  Assert.Fail();	
  
}	
  
25	
  
Тестирование	
  Rx	
  
public	
  ReactiveCollection(Func<TItem,	
  string,	
  bool>	
  filterFunc)
:	
  this(filterFunc,	
  NewThreadScheduler.Default,	
  DispatcherScheduler.Current,	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  )	
  	
  {	
  	
  }
	
  
	
  	
  	
  	
  	
  public	
  ReactiveCollection(Func<TItem,	
  string,	
  bool>	
  filterFunc,	
  IScheduler	
  scheduler)	
  
	
  	
  	
  	
  	
  :	
  this(filterFunc,	
  scheduler,	
  scheduler,	
  scheduler)	
  {	
  }	
  	
  
	
  public	
  ReactiveCollection(Func<TItem,	
  string,	
  bool>	
  filterFunc,
	
  	
  	
  	
  	
  IScheduler	
  filterScheduler,	
  IScheduler	
  observeOn,	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  )	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  buffer	
  =	
  new	
  ObservableCollection<TItem>();
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _subscription	
  =	
  this.PropertyChangedAsObservable(x	
  =>	
  x.Filter)
.Throttle(TimeSpan.FromMilliseconds(timeout),	
  timerScheduler)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Where(_	
  =>	
  string.IsNullOrWhiteSpace(Filter)	
  ||	
  Filter.Length	
  >	
  3)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .DistinctUntilChanged(_	
  =>	
  Filter)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Merge(this.PropertyChangedAsObservable(x	
  =>	
  x.Source))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Do(_	
  =>	
  buffer.Clear())
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Select(_	
  =>	
  Source.EmptyIfNull()
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .ToObservable(filterScheduler)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Where(x	
  =>	
  filterFunc(x,	
  Filter))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Buffer(TimeSpan.FromMilliseconds(200),	
  timerScheduler)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Do(x	
  =>	
  x.ForEach(buffer.Add)))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Switch()
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .ObserveOn(observeOn)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  .Subscribe(_	
  =>	
  View	
  =	
  buffer.ToArray());
	
  }
Scheduler.Default	
  
IScheduler	
  timerScheduler	
  
26	
  
Тестирование	
  Rx	
  
[Test]
public	
  void	
  OnFilter_FilterView()
{
	
  	
  	
  	
  var	
  testScheduler	
  =	
  new	
  TestScheduler();
	
  	
  	
  	
  var	
  collection	
  =	
  new	
  ReactiveCollection<string>(	
  
	
  (x,	
  y)	
  =>	
  x.Contains(y),	
  testScheduler)
	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Source	
  =	
  new[]	
  {"client",	
  "bad	
  client",	
  "item",	
  "second	
  item"},
	
  	
  	
  	
  };
	
  	
  	
  	
  	
  collection.Filter	
  =	
  "item";
	
  	
  	
  	
  	
  testScheduler.Start();
	
  	
  	
  	
  	
  Assert.IsTrue(new[]	
  {"item",	
  "second	
  item"}.SequenceEqual(collection.View));
}	
  
27	
  
Тестирование	
  Rx	
  
[Test]
public	
  void	
  OnFilter_RunFiltrationInAnotherThread()
{
	
  	
  	
  	
  var	
  testScheduler	
  =	
  new	
  TestScheduler();
	
  	
  	
  	
  var	
  filterThreadId	
  =	
  0;
	
  	
  	
  	
  new	
  ReactiveCollection<int>((x,	
  y)	
  =>
	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  filterThreadId	
  =	
  Thread.CurrentThread.ManagedThreadId;
	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  true;
	
  	
  	
  	
  },	
  NewThreadScheduler.Default,	
  testScheduler,	
  testScheduler)
	
  	
  	
  	
  collection.Source	
  =	
  new	
  int[10];	
  
	
  
	
  	
  	
  	
  testScheduler.Start();
	
  	
  	
  	
  Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId,	
  filterThreadId);
}	
  
28	
  
Тестирование	
  Rx	
  
[Test]
public	
  void	
  OnSlowFilter_BufferResults()
{
	
  	
  	
  	
  var	
  testScheduler	
  =	
  new	
  TestScheduler();
	
  	
  	
  	
  var	
  filterCounter	
  =	
  0;
	
  	
  	
  	
  var	
  collection	
  =	
  new	
  ReactiveCollection<int>((x,	
  y)	
  =>
	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  if	
  (++filterCounter%3	
  ==	
  0)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  testScheduler.Sleep(TimeSpan.FromMilliseconds(250).Ticks);
	
  	
  	
  	
  	
  	
  	
  	
  return	
  true;
	
  	
  	
  	
  },	
  testScheduler)
	
  	
  	
  	
  collection.Source	
  =	
  new	
  int[10];	
  
	
  
	
  	
  	
  	
  collection.PropertyChangedAsObservable(x	
  =>	
  x.View)
	
  	
  	
  	
  	
  	
  	
  	
  	
  .Take(3)
	
  	
  	
  	
  	
  	
  	
  	
  	
  .Subscribe(x	
  =>	
  Assert.IsTrue(collection.View.Length%3	
  ==	
  0));
	
  	
  	
  	
  testScheduler.Start();
	
  	
  	
  	
  Assert.AreEqual(10,	
  collection.View.Length);
}	
  
29	
  
Тестирование	
  Rx	
  
[Test]
public	
  void	
  OnSourceChanged_CallFilter_EvenWithinThreshold()
{
	
  	
  	
  	
  var	
  testScheduler	
  =	
  new	
  TestScheduler();
	
  	
  	
  	
  var	
  filterCounter	
  =	
  0;
	
  	
  	
  	
  var	
  collection	
  =	
  new	
  ReactiveCollection<int>((x,	
  y)	
  =>
	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  filterCounter++;
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  true;
	
  	
  	
  	
  },	
  testScheduler)
	
  
	
  	
  	
  	
  for	
  (var	
  i	
  =	
  0;	
  i	
  <	
  10;	
  i++)
	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  collection.Source	
  =	
  new	
  int[1];
	
  	
  	
  	
  	
  	
  	
  	
  testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(100).Ticks);
	
  	
  	
  	
  }	
  
	
  	
  	
  	
  Assert.AreEqual(10,	
  filterCounter);
}	
  
30	
  
Тестирование	
  Rx	
  
[Test]
public	
  void	
  OnSameFilter_DoNotCallFilter()
{
	
  	
  	
  	
  var	
  testScheduler	
  =	
  new	
  TestScheduler();
	
  	
  	
  	
  var	
  filterCounter	
  =	
  0;
	
  	
  	
  	
  var	
  collection	
  =	
  new	
  ReactiveCollection<int>((x,	
  y)	
  =>
	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  filterCounter++;
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  true;
	
  	
  	
  	
  },	
  testScheduler)
	
  	
  	
  	
  collection.Source	
  =	
  new	
  int[1];	
  
	
  	
  	
  	
  collection.Filter	
  =	
  "firstFilter";	
  
	
  	
  	
  	
  testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(250).Ticks);	
  
	
  
	
  	
  	
  	
  filterCounter	
  =	
  0;	
  
	
  	
  	
  	
  for	
  (var	
  i	
  =	
  0;	
  i	
  <	
  10;	
  i++)
	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  collection.Filter	
  =	
  "secondFilter";
	
  	
  	
  	
  	
  	
  	
  	
  testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(150).Ticks);
	
  	
  	
  	
  	
  	
  	
  	
  collection.Filter	
  =	
  "firstFilter";
	
  	
  	
  	
  	
  	
  	
  	
  testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(250).Ticks);
	
  	
  	
  	
  }
	
  	
  	
  	
  Assert.AreEqual(0,	
  filterCounter);
}	
  
31	
  
Тестирование	
  Rx	
  [TestCase(100,	
  false)]
[TestCase(150,	
  false)]
[TestCase(250,	
  true)]
[TestCase(500,	
  true)]
public	
  void	
  OnMultipleChangesWithinThreshold_DoNotCallFilter(	
  
	
   	
   	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  int	
  threshold,	
  bool	
  runFilter)
{
	
  	
  	
  	
  var	
  testScheduler	
  =	
  new	
  TestScheduler();
	
  	
  	
  	
  var	
  filterCounter	
  =	
  0;
	
  	
  	
  	
  var	
  collection	
  =	
  new	
  ReactiveCollection<int>((x,	
  y)	
  =>
	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  filterCounter++;
	
  	
  	
  	
  	
  	
  	
  	
  return	
  true;
	
  	
  	
  	
  },	
  testScheduler)	
  {Source	
  =	
  new	
  int[1]};	
  
	
  	
  	
  	
  testScheduler.AdvanceBy(TimeSpan.FromMinutes(1).Ticks);	
  
	
  
	
  	
  	
  	
  filterCounter	
  =	
  0;
	
  	
  	
  	
  for	
  (var	
  i	
  =	
  0;	
  i	
  <	
  10;	
  i++)
	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  collection.Filter	
  +=	
  "filter";
	
  	
  	
  	
  	
  	
  	
  	
  testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(threshold).Ticks);
	
  	
  	
  	
  }	
  
	
  	
  	
  	
  if	
  (runFilter)	
  
	
  	
  	
  	
  	
  	
  	
  	
  Assert.AreNotEqual(0,	
  filterCounter);	
  
	
  	
  	
  	
  else	
  
	
  	
  	
  	
  	
  	
  	
  	
  Assert.AreEqual(0,	
  filterCounter);	
  
}	
   32	
  

More Related Content

What's hot

Интуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с даннымиИнтуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с даннымиГлеб Тарасов
 
Apache spark
Apache sparkApache spark
Apache spark
Anton Anokhin
 
Multiprocessor Programming Intro (lecture 3)
Multiprocessor Programming Intro (lecture 3)Multiprocessor Programming Intro (lecture 3)
Multiprocessor Programming Intro (lecture 3)Dmitry Tsitelov
 
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru GroupКак не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Mail.ru Group
 
Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8
Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8
Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8
Andrey Karpov
 
Developing highload servers with Java
Developing highload servers with JavaDeveloping highload servers with Java
Developing highload servers with Java
Andrei Pangin
 
Контейнеры и хранение объектов в ООП
Контейнеры и хранение объектов в ООПКонтейнеры и хранение объектов в ООП
Контейнеры и хранение объектов в ООПitclub_kz
 
Владимир Горбенко «Использование блоков в Objective-C»
Владимир Горбенко «Использование блоков в Objective-C»Владимир Горбенко «Использование блоков в Objective-C»
Владимир Горбенко «Использование блоков в Objective-C»e-Legion
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture libraryMERA_school
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Python Meetup
 
Lec 3
Lec 3Lec 3
Swift School #2
Swift School #2Swift School #2
Swift School #2
Sergey Pronin
 
Java осень 2014 занятие 7
Java осень 2014 занятие 7Java осень 2014 занятие 7
Java осень 2014 занятие 7
Technopark
 
C#. От основ к эффективному коду
C#. От основ к эффективному кодуC#. От основ к эффективному коду
C#. От основ к эффективному коду
Vasiliy Deynega
 
Something about Golang
Something about GolangSomething about Golang
Something about Golang
Anton Arhipov
 
Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптер
Sergey Platonov
 
Хранимые процедуры в NoSQL СУБД на примере Tarantool / Денис Линник (Mail.Ru)
Хранимые процедуры в NoSQL СУБД на примере Tarantool / Денис Линник (Mail.Ru)Хранимые процедуры в NoSQL СУБД на примере Tarantool / Денис Линник (Mail.Ru)
Хранимые процедуры в NoSQL СУБД на примере Tarantool / Денис Линник (Mail.Ru)
Ontico
 
Svitla .Net meetup in Kiev, Anzhiiak Oleksii
Svitla .Net meetup in Kiev, Anzhiiak OleksiiSvitla .Net meetup in Kiev, Anzhiiak Oleksii
Svitla .Net meetup in Kiev, Anzhiiak Oleksii
Svitla Systems Inc.
 
Java осень 2013 лекция 2
Java осень 2013 лекция 2Java осень 2013 лекция 2
Java осень 2013 лекция 2Technopark
 
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev FedorProgramming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Fedor Lavrentyev
 

What's hot (20)

Интуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с даннымиИнтуит. Разработка приложений для iOS. Лекция 8. Работа с данными
Интуит. Разработка приложений для iOS. Лекция 8. Работа с данными
 
Apache spark
Apache sparkApache spark
Apache spark
 
Multiprocessor Programming Intro (lecture 3)
Multiprocessor Programming Intro (lecture 3)Multiprocessor Programming Intro (lecture 3)
Multiprocessor Programming Intro (lecture 3)
 
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru GroupКак не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
 
Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8
Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8
Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8
 
Developing highload servers with Java
Developing highload servers with JavaDeveloping highload servers with Java
Developing highload servers with Java
 
Контейнеры и хранение объектов в ООП
Контейнеры и хранение объектов в ООПКонтейнеры и хранение объектов в ООП
Контейнеры и хранение объектов в ООП
 
Владимир Горбенко «Использование блоков в Objective-C»
Владимир Горбенко «Использование блоков в Objective-C»Владимир Горбенко «Использование блоков в Objective-C»
Владимир Горбенко «Использование блоков в Objective-C»
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture library
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Lec 3
Lec 3Lec 3
Lec 3
 
Swift School #2
Swift School #2Swift School #2
Swift School #2
 
Java осень 2014 занятие 7
Java осень 2014 занятие 7Java осень 2014 занятие 7
Java осень 2014 занятие 7
 
C#. От основ к эффективному коду
C#. От основ к эффективному кодуC#. От основ к эффективному коду
C#. От основ к эффективному коду
 
Something about Golang
Something about GolangSomething about Golang
Something about Golang
 
Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптер
 
Хранимые процедуры в NoSQL СУБД на примере Tarantool / Денис Линник (Mail.Ru)
Хранимые процедуры в NoSQL СУБД на примере Tarantool / Денис Линник (Mail.Ru)Хранимые процедуры в NoSQL СУБД на примере Tarantool / Денис Линник (Mail.Ru)
Хранимые процедуры в NoSQL СУБД на примере Tarantool / Денис Линник (Mail.Ru)
 
Svitla .Net meetup in Kiev, Anzhiiak Oleksii
Svitla .Net meetup in Kiev, Anzhiiak OleksiiSvitla .Net meetup in Kiev, Anzhiiak Oleksii
Svitla .Net meetup in Kiev, Anzhiiak Oleksii
 
Java осень 2013 лекция 2
Java осень 2013 лекция 2Java осень 2013 лекция 2
Java осень 2013 лекция 2
 
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev FedorProgramming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
 

Viewers also liked

Конструкторы.Деструкторы.Динамическое распределение памяти
Конструкторы.Деструкторы.Динамическое распределение памятиКонструкторы.Деструкторы.Динамическое распределение памяти
Конструкторы.Деструкторы.Динамическое распределение памятиREX-MDK
 
Интерфейсы
ИнтерфейсыИнтерфейсы
ИнтерфейсыREX-MDK
 
Делегаты
ДелегатыДелегаты
ДелегатыREX-MDK
 
Обобщенные классы в C#
Обобщенные классы в C#Обобщенные классы в C#
Обобщенные классы в C#REX-MDK
 
Anemic Domain Model - антипаттерн или SOLID?
Anemic Domain Model - антипаттерн или SOLID?Anemic Domain Model - антипаттерн или SOLID?
Anemic Domain Model - антипаттерн или SOLID?
GoSharp
 
TPL Dataflow – зачем и для кого?
TPL Dataflow – зачем и для кого?TPL Dataflow – зачем и для кого?
TPL Dataflow – зачем и для кого?
GoSharp
 
Основы Java. 4. Collection Framework
Основы Java. 4. Collection FrameworkОсновы Java. 4. Collection Framework
Основы Java. 4. Collection Framework
Sergey Nemchinsky
 
ReSharper: прошлое и будущее
ReSharper: прошлое и будущееReSharper: прошлое и будущее
ReSharper: прошлое и будущее
GoSharp
 
Программируем быстрее с CodeRush
Программируем быстрее с CodeRushПрограммируем быстрее с CodeRush
Программируем быстрее с CodeRush
GoSharp
 
Как попасть на следующий уровень карьеры и зарплаты в C#
Как попасть на следующий уровень карьеры и зарплаты в C#Как попасть на следующий уровень карьеры и зарплаты в C#
Как попасть на следующий уровень карьеры и зарплаты в C#
GoSharp
 

Viewers also liked (10)

Конструкторы.Деструкторы.Динамическое распределение памяти
Конструкторы.Деструкторы.Динамическое распределение памятиКонструкторы.Деструкторы.Динамическое распределение памяти
Конструкторы.Деструкторы.Динамическое распределение памяти
 
Интерфейсы
ИнтерфейсыИнтерфейсы
Интерфейсы
 
Делегаты
ДелегатыДелегаты
Делегаты
 
Обобщенные классы в C#
Обобщенные классы в C#Обобщенные классы в C#
Обобщенные классы в C#
 
Anemic Domain Model - антипаттерн или SOLID?
Anemic Domain Model - антипаттерн или SOLID?Anemic Domain Model - антипаттерн или SOLID?
Anemic Domain Model - антипаттерн или SOLID?
 
TPL Dataflow – зачем и для кого?
TPL Dataflow – зачем и для кого?TPL Dataflow – зачем и для кого?
TPL Dataflow – зачем и для кого?
 
Основы Java. 4. Collection Framework
Основы Java. 4. Collection FrameworkОсновы Java. 4. Collection Framework
Основы Java. 4. Collection Framework
 
ReSharper: прошлое и будущее
ReSharper: прошлое и будущееReSharper: прошлое и будущее
ReSharper: прошлое и будущее
 
Программируем быстрее с CodeRush
Программируем быстрее с CodeRushПрограммируем быстрее с CodeRush
Программируем быстрее с CodeRush
 
Как попасть на следующий уровень карьеры и зарплаты в C#
Как попасть на следующий уровень карьеры и зарплаты в C#Как попасть на следующий уровень карьеры и зарплаты в C#
Как попасть на следующий уровень карьеры и зарплаты в C#
 

Similar to Живые приложения с Rx

Reactive extensions
Reactive extensionsReactive extensions
Reactive extensions
Sergey Teplyakov
 
Reactive Extensions
Reactive ExtensionsReactive Extensions
Reactive Extensions
GetDev.NET
 
Курсы актерского мастерства
Курсы актерского мастерстваКурсы актерского мастерства
Курсы актерского мастерства
Vasil Remeniuk
 
Solit 2014, Реактивный Javascript. Победа над асинхронностью и вложенностью, ...
Solit 2014, Реактивный Javascript. Победа над асинхронностью и вложенностью, ...Solit 2014, Реактивный Javascript. Победа над асинхронностью и вложенностью, ...
Solit 2014, Реактивный Javascript. Победа над асинхронностью и вложенностью, ...
solit
 
DUMP-2015: «Распределенная обработка миллионов документов на Scala и Akka» Ст...
DUMP-2015: «Распределенная обработка миллионов документов на Scala и Akka» Ст...DUMP-2015: «Распределенная обработка миллионов документов на Scala и Akka» Ст...
DUMP-2015: «Распределенная обработка миллионов документов на Scala и Akka» Ст...
it-people
 
Java8. Innovations
Java8. InnovationsJava8. Innovations
Java8. Innovations
Nakraynikov Oleg
 
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Sigma Software
 
Agile Instrumentation
Agile InstrumentationAgile Instrumentation
Agile Instrumentation
Mikalai_Kardash
 
Шаблоны проектирования 1
Шаблоны проектирования 1Шаблоны проектирования 1
Шаблоны проектирования 1
Constantin Kichinsky
 
Codefest-2015 Reactive Streams
Codefest-2015 Reactive StreamsCodefest-2015 Reactive Streams
Codefest-2015 Reactive Streams
Alexey Romanchuk
 
как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...
как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...
как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...rit2011
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6Dmitry Soshnikov
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8
chashnikov
 
Михаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. АсинхронностьМихаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. Асинхронность
Yandex
 
Как приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложенияхКак приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложениях
Denis Tsvettsih
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.
Roman Brovko
 
Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"
Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"
Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"
IT Event
 

Similar to Живые приложения с Rx (20)

Reactive extensions
Reactive extensionsReactive extensions
Reactive extensions
 
Reactive Extensions
Reactive ExtensionsReactive Extensions
Reactive Extensions
 
ZooKeeper Java Cloud
ZooKeeper Java CloudZooKeeper Java Cloud
ZooKeeper Java Cloud
 
Курсы актерского мастерства
Курсы актерского мастерстваКурсы актерского мастерства
Курсы актерского мастерства
 
Solit 2014, Реактивный Javascript. Победа над асинхронностью и вложенностью, ...
Solit 2014, Реактивный Javascript. Победа над асинхронностью и вложенностью, ...Solit 2014, Реактивный Javascript. Победа над асинхронностью и вложенностью, ...
Solit 2014, Реактивный Javascript. Победа над асинхронностью и вложенностью, ...
 
Zagursky
ZagurskyZagursky
Zagursky
 
DUMP-2015: «Распределенная обработка миллионов документов на Scala и Akka» Ст...
DUMP-2015: «Распределенная обработка миллионов документов на Scala и Akka» Ст...DUMP-2015: «Распределенная обработка миллионов документов на Scala и Akka» Ст...
DUMP-2015: «Распределенная обработка миллионов документов на Scala и Akka» Ст...
 
Rx
RxRx
Rx
 
Java8. Innovations
Java8. InnovationsJava8. Innovations
Java8. Innovations
 
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
 
Agile Instrumentation
Agile InstrumentationAgile Instrumentation
Agile Instrumentation
 
Шаблоны проектирования 1
Шаблоны проектирования 1Шаблоны проектирования 1
Шаблоны проектирования 1
 
Codefest-2015 Reactive Streams
Codefest-2015 Reactive StreamsCodefest-2015 Reactive Streams
Codefest-2015 Reactive Streams
 
как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...
как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...
как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6
 
Lambdas in java 8
Lambdas in java 8Lambdas in java 8
Lambdas in java 8
 
Михаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. АсинхронностьМихаил Давыдов - JavaScript. Асинхронность
Михаил Давыдов - JavaScript. Асинхронность
 
Как приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложенияхКак приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложениях
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.
 
Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"
Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"
Артемий Гарин "Выбор лучшего хранилища в Android (cпойлер: Realm)"
 

More from GoSharp

Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
GoSharp
 
UniversalApp "убийца" WPF или же это WPF+ ?
UniversalApp "убийца" WPF или же это WPF+ ?UniversalApp "убийца" WPF или же это WPF+ ?
UniversalApp "убийца" WPF или же это WPF+ ?
GoSharp
 
UI тестирование WPF приложений в Дойче Банке
UI тестирование WPF приложений в Дойче БанкеUI тестирование WPF приложений в Дойче Банке
UI тестирование WPF приложений в Дойче Банке
GoSharp
 
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
GoSharp
 
За что не любить EF и чем его заменить
За что не любить EF и чем его заменитьЗа что не любить EF и чем его заменить
За что не любить EF и чем его заменить
GoSharp
 
MVVM в WinForms – DevExpress Way (теория и практика)
MVVM в WinForms – DevExpress Way (теория и практика)MVVM в WinForms – DevExpress Way (теория и практика)
MVVM в WinForms – DevExpress Way (теория и практика)
GoSharp
 
Паттерны быстрой разработки WPF MVVM бизнес-приложений
Паттерны быстрой разработки WPF MVVM бизнес-приложенийПаттерны быстрой разработки WPF MVVM бизнес-приложений
Паттерны быстрой разработки WPF MVVM бизнес-приложенийGoSharp
 
Gosharp Intro
Gosharp IntroGosharp Intro
Gosharp Intro
GoSharp
 
Проектирование сетевой инфраструктуры под SOA проекты ASP.NET
Проектирование сетевой инфраструктуры под SOA проекты ASP.NETПроектирование сетевой инфраструктуры под SOA проекты ASP.NET
Проектирование сетевой инфраструктуры под SOA проекты ASP.NET
GoSharp
 
Мониторинг приложений ASP.NET на основе сервиса Application Insights
Мониторинг приложений ASP.NET на основе сервиса Application InsightsМониторинг приложений ASP.NET на основе сервиса Application Insights
Мониторинг приложений ASP.NET на основе сервиса Application Insights
GoSharp
 
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETОпыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
GoSharp
 
ASP.NET Internals
ASP.NET InternalsASP.NET Internals
ASP.NET Internals
GoSharp
 
Кросплатформенная разработка на ASP.NET vNext
Кросплатформенная разработка на ASP.NET vNextКросплатформенная разработка на ASP.NET vNext
Кросплатформенная разработка на ASP.NET vNext
GoSharp
 
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNext
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNextВнедрение зависимостей в ASP.NET MVС и ASP.NET vNext
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNext
GoSharp
 
Будущее ASP.NET
Будущее ASP.NETБудущее ASP.NET
Будущее ASP.NET
GoSharp
 
Коучинг команд разработки и коучинговые инструменты в работе тимлида
Коучинг команд разработки и коучинговые инструменты в работе тимлидаКоучинг команд разработки и коучинговые инструменты в работе тимлида
Коучинг команд разработки и коучинговые инструменты в работе тимлида
GoSharp
 
Взаимное влияние Source Code Management и других средств организации разработки
Взаимное влияние Source Code Management и других средств организации разработкиВзаимное влияние Source Code Management и других средств организации разработки
Взаимное влияние Source Code Management и других средств организации разработки
GoSharp
 
DevOPS инструменты для .NET проектов
DevOPS инструменты для .NET проектовDevOPS инструменты для .NET проектов
DevOPS инструменты для .NET проектов
GoSharp
 
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компании
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компанииДоски проектов и продуктов на TFS: Agile-визуализация на уровне компании
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компании
GoSharp
 
Архитектурные решения при создании облачного сервиса на Asp.Net
Архитектурные решения при создании облачного сервиса на Asp.NetАрхитектурные решения при создании облачного сервиса на Asp.Net
Архитектурные решения при создании облачного сервиса на Asp.Net
GoSharp
 

More from GoSharp (20)

Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
 
UniversalApp "убийца" WPF или же это WPF+ ?
UniversalApp "убийца" WPF или же это WPF+ ?UniversalApp "убийца" WPF или же это WPF+ ?
UniversalApp "убийца" WPF или же это WPF+ ?
 
UI тестирование WPF приложений в Дойче Банке
UI тестирование WPF приложений в Дойче БанкеUI тестирование WPF приложений в Дойче Банке
UI тестирование WPF приложений в Дойче Банке
 
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
 
За что не любить EF и чем его заменить
За что не любить EF и чем его заменитьЗа что не любить EF и чем его заменить
За что не любить EF и чем его заменить
 
MVVM в WinForms – DevExpress Way (теория и практика)
MVVM в WinForms – DevExpress Way (теория и практика)MVVM в WinForms – DevExpress Way (теория и практика)
MVVM в WinForms – DevExpress Way (теория и практика)
 
Паттерны быстрой разработки WPF MVVM бизнес-приложений
Паттерны быстрой разработки WPF MVVM бизнес-приложенийПаттерны быстрой разработки WPF MVVM бизнес-приложений
Паттерны быстрой разработки WPF MVVM бизнес-приложений
 
Gosharp Intro
Gosharp IntroGosharp Intro
Gosharp Intro
 
Проектирование сетевой инфраструктуры под SOA проекты ASP.NET
Проектирование сетевой инфраструктуры под SOA проекты ASP.NETПроектирование сетевой инфраструктуры под SOA проекты ASP.NET
Проектирование сетевой инфраструктуры под SOA проекты ASP.NET
 
Мониторинг приложений ASP.NET на основе сервиса Application Insights
Мониторинг приложений ASP.NET на основе сервиса Application InsightsМониторинг приложений ASP.NET на основе сервиса Application Insights
Мониторинг приложений ASP.NET на основе сервиса Application Insights
 
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETОпыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
 
ASP.NET Internals
ASP.NET InternalsASP.NET Internals
ASP.NET Internals
 
Кросплатформенная разработка на ASP.NET vNext
Кросплатформенная разработка на ASP.NET vNextКросплатформенная разработка на ASP.NET vNext
Кросплатформенная разработка на ASP.NET vNext
 
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNext
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNextВнедрение зависимостей в ASP.NET MVС и ASP.NET vNext
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNext
 
Будущее ASP.NET
Будущее ASP.NETБудущее ASP.NET
Будущее ASP.NET
 
Коучинг команд разработки и коучинговые инструменты в работе тимлида
Коучинг команд разработки и коучинговые инструменты в работе тимлидаКоучинг команд разработки и коучинговые инструменты в работе тимлида
Коучинг команд разработки и коучинговые инструменты в работе тимлида
 
Взаимное влияние Source Code Management и других средств организации разработки
Взаимное влияние Source Code Management и других средств организации разработкиВзаимное влияние Source Code Management и других средств организации разработки
Взаимное влияние Source Code Management и других средств организации разработки
 
DevOPS инструменты для .NET проектов
DevOPS инструменты для .NET проектовDevOPS инструменты для .NET проектов
DevOPS инструменты для .NET проектов
 
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компании
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компанииДоски проектов и продуктов на TFS: Agile-визуализация на уровне компании
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компании
 
Архитектурные решения при создании облачного сервиса на Asp.Net
Архитектурные решения при создании облачного сервиса на Asp.NetАрхитектурные решения при создании облачного сервиса на Asp.Net
Архитектурные решения при создании облачного сервиса на Asp.Net
 

Живые приложения с Rx

  • 1.
  • 2. Почему  Reac,ve  Extensions?   •  Отзывчивость  приложений   •  Упрощение  кода  работы  со  временем   •  Упрощение  управления  потоками   •  Упрощение  подписки-­‐отписки  для  событий   •  Тестируемость  кода   •  LINQ  для  событий  и  потоков  данных   •  Наличие  реализаций  для  других  языков   программирования   2  
  • 4. Преимущества  Rx  перед  событиями   •  События  однопоточны.  Все  обработчики  будут  вызываться  по  очереди   в  потоке  который  запустил  событие.   •  Если  в  одном  из  обработчиков  события  будет  выброшено  исключение   все  остальные  обработчики  не  будут  вызваны   •  Все  подписки  на  события  необходимо  отписывать.  Для  этого  придется   хранить  ссылку  на  метод  для  отписки  и  объект   •  Rx  поддерживает  async/await  для  запросов  требующих  ожидания   конкретного  элемента   •  Rx  предоставляет  методы  для  управления  временем  и  потоками,  а   также  тестовые  реализации  управляющих  объектов.     •  Rx  предоставляет  методы  для  связи  нескольких  потоков  данных  в   один   •  Rx  предоставляет  возможность  обрабатывать  данные  аналогично   LINQ-­‐запросу  в  функциональном  стиле.   4  
  • 5. Основные  интерфейсы  Rx   •  IObservable<T>        IDisposable  Subscribe(IObserver<T>  observer);   •  IObserver<T>    void  OnNext(T  value);    void  OnError(Exception  error);    void  OnCompleted();   •  IScheduler   •  ISubject<T>  :  IObservable<T>, IObserver<T> 5  
  • 6. Простой  класс  с  Rx          public  class  LogoutManager          {                  public  IObservable<Unit>  Logout  {  get;  private  set;  }                  public  IObserver<Unit>  UserActionsObserver  {  get;  private  set;  }                  public  IObserver<Unit>  LogoutCommandsObserver  {  get;  private  set;  }                      public  LogoutManager(TimeSpan  timeout)                  {                          var  userActionsSubject  =  new  Subject<Unit>();                          var  logoutSubject  =  new  Subject<Unit>();                            UserActionsObserver  =  userActionsSubject.AsObserver();                          LogoutCommandsObserver  =  logoutSubject.AsObserver();                            Logout  =  userActionsSubject                                  .StartWith(Unit.Default)                                  .Throttle(timeout)                                  .Merge(logoutSubject);                  }          }   6  
  • 7. Потоки  данных  в  LogoutManager    Logout  =  userActionsSubject                            .StartWith(Unit.Default)                            .Throttle(timeout)                            .Merge(logoutSubject);   7  
  • 8. Интеграция  Rx  с  существующими   событиями   •  Короткая:   Observable.FromEventPattern(source,  "PropertyChanged");   •  Типобезопасная:   Observable.FromEventPattern   <PropertyChangedEventHandler,PropertyChangedEventArgs>(              handler  =>  source.PropertyChanged  +=  handler,                handler  =>  source.PropertyChanged  -­‐=  handler)   •  Промежуточные  варианты  с  указанием  типов  только   для  части  операндов       8  
  • 10. Текстовый  фильтр  на  Rx    Observable.FromEventPattern   <TextChangedEventHandler,  TextChangedEventArgs>(    handler  =>  filterTextBox.TextChanged  +=  handler,    handler  =>  filterTextBox.TextChanged  -­‐=  handler)            .Select(x  =>  x.Sender)            .Cast<TextBox>()            .Select(x  =>  x.Text)            .Where(x  =>  x.Length  >  2)      .Subscribe(FilterCollection);   10  
  • 11. Расширенный  фильтр  на  Rx   •  Исходная  коллекция  также  может  меняться   •  Пока  пользователь  продолжает  ввод  не  имеет  смысла   перезапускать  поиск   •  Логика  фильтрации  может  быть  достаточно  сложна  и   отнимать  много  времени   •  Пользователь  должен  видеть  результаты  по  мере  их   поступления,  а  не  после  завершения  фильтрации   •  Пользователь  может  начать  вводить  фильтр,  а  затем   вернуть  старый.  Повторную  фильтрацию  при  этом  не   надо  запускать   11  
  • 12. Расширенный  фильтр  на  Rx    public  interface  IReactiveCollection<TItem>  :            INotifyPropertyChanged,  IDisposable {    //  Источник  данных  (полная  коллекция)                  TItem[]  Source  {  get;  set;  }                    //  Отображаемая  часть  данных  (отфильтрованная  коллекция)                  TItem[]  View  {  get;  }                    //  Строка  фильтра                  string  Filter  {  get;  set;  }              }   12  
  • 13. Расширенный  фильтр  на  Rx   public  ReactiveCollection(Func<TItem,  string,  bool>  filterFunc)  {          var  buffer  =  new  ObservableCollection<TItem>();            _subscription  =  this.PropertyChangedAsObservable(x  =>  x.Filter)                    .Throttle(TimeSpan.FromMilliseconds(200))                    .Where(_  =>  string.IsNullOrWhiteSpace(Filter)  ||  Filter.Length  >  2)                    .DistinctUntilChanged(_  =>  Filter)                    .Merge(this.PropertyChangedAsObservable(x  =>  x.Source))                    .Do(_  =>  buffer.Clear())                    .Select(_  =>  Source.EmptyIfNull()                            .ToObservable()                            .Where(x  =>  filterFunc(x,  Filter))                            .Buffer(TimeSpan.FromMilliseconds(200))                            .Do(x  =>  x.ForEach(buffer.Add)))                    .Switch()                    .Subscribe(_  =>  View  =  buffer.ToArray());   } 13  
  • 14. Потоки  данных  в  расширенном   фильтре   this.PropertyChangedAsObservable(x  =>  x.Filter)                    .Throttle(TimeSpan.FromMilliseconds(200))                    .Where(_  =>  string.IsNullOrWhiteSpace(Filter)  ||  Filter.Length  >  2)                    .DistinctUntilChanged(_  =>  Filter)                    .Merge(this.PropertyChangedAsObservable(x  =>  x.Source)) 14  
  • 15. Потоки  данных  в  расширенном   фильтре      .Select(_  =>  Source.EmptyIfNull()                            .ToObservable()                            .Where(x  =>  filterFunc(x,  Filter))                            .Buffer(TimeSpan.FromMilliseconds(200))                            .Do(x  =>  x.ForEach(buffer.Add)))                  .Switch()   15  
  • 16. Управление  потоками  в  Rx   public  ReactiveCollection(Func<TItem,  string,  bool>  filterFunc,                                                                                          )  {          var  buffer  =  new  ObservableCollection<TItem>();            _subscription  =  this.PropertyChangedAsObservable(x  =>  x.Filter)                    .Throttle(TimeSpan.FromMilliseconds(timeout))                    .Where(_  =>  string.IsNullOrWhiteSpace(Filter)  ||  Filter.Length  >  3)                    .DistinctUntilChanged(_  =>  Filter)                    .Merge(this.PropertyChangedAsObservable(x  =>  x.Source))                    .Do(_  =>  buffer.Clear())                    .Select(_  =>  Source.EmptyIfNull()                            .ToObservable(                              )                            .Where(x  =>  filterFunc(x,  Filter))                            .Buffer(TimeSpan.FromMilliseconds(timeout))                            .Do(x  =>  x.ForEach(buffer.Add)))                    .Switch()                      .ObserveOn(observeOn)                    .Subscribe(_  =>  View  =  buffer.ToArray());   } IScheduler  filterScheduler,  IScheduler  observeOn     filterScheduler   16  
  • 18. Проблемы  реализации  при  помощи   событий   •  Необходимость  реализации  задержки  для   событий  –  для  того,  чтобы  не  загружать  списки   клиентов  и  счетов  много  раз   •  Постоянные  подписки  и  отписки  на  события   элементов  коллекции  клиентов,  отсутствие   отписок  быстро  приведет  к  утечке  памяти  в   приложении.   •  Код  для  управления  потоками  –  фильтрация  в   отдельном  потоке,  вывод  результатов  в  другом   18  
  • 19. Применение  фильтра  на  Rx   Companies  =  new  ReactiveCollection<Company>((x,  y)  =>                                                          string.IsNullOrWhiteSpace(y)  ||  x.Name.Contains(y))  {          Source  =  CompanyList        };      Clients  =  new  ReactiveCollection<Client>((x,  y)  =>                    string.IsNullOrWhiteSpace(y)  ||  x.Name.Contains(y));    Accounts  =  new  ReactiveCollection<Account>(SlowFilter);    private  bool  SlowFilter(Account  x,  string  y)  {    Thread.Sleep(random.Next(50,  250));    return  string.IsNullOrWhiteSpace(y)  ||  x.Number.ToString().Contains(y);  }   19  
  • 20. Применение  фильтра  на  Rx    Companies.Source.Select(x  =>  x.PropertyChangedAsObservable(y  =>  y.Selected))            .Merge()            .Throttle(TimeSpan.FromMilliseconds(200))            .ObserveOnDispatcher()            .Subscribe(_  =>  Clients.Source  =  GetClients(Companies.Source                          .Where(x  =>  x.Selected)                          .Select(x  =>  x.Id)));      Clients.PropertyChangedAsObservable(x  =>  x.Source)        .Select(_  =>  Clients.Source)        .Select(x  =>  x.Select(y  =>  y.PropertyChangedAsObservable(z  =>  z.Selected))                    .Merge()                    .Throttle(TimeSpan.FromMilliseconds(100)))        .Switch()        .ObserveOnDispatcher()        .Subscribe(_  =>  Accounts.Source  =  GetAccountSource(Clients.Source                        .Where(x  =>  x.Selected)                        .Select(x  =>  x.Id)));   20  
  • 21. А  что  с  тестированием?   •  Управление  временем   •  Управление  потоками   •  Буферизация     •  Скорость  выполнения  тестов   21  
  • 22. Тестирование  Rx  при  помощи   интерфейса  IScheduler          public  class  LogoutManager          {                  public  IObservable<Unit>  Logout  {  get;  private  set;  }                  public  IObserver<Unit>  UserActionsObserver  {  get;  private  set;  }                  public  IObserver<Unit>  LogoutCommandsObserver  {  get;  private  set;  }                      public  LogoutManager(TimeSpan  timeout,                                            )                  {                          var  userActionsSubject  =  new  Subject<Unit>();                          var  logoutSubject  =  new  Subject<Unit>();                            UserActionsObserver  =  userActionsSubject.AsObserver();                          LogoutCommandsObserver  =  logoutSubject.AsObserver();                            Logout  =  userActionsSubject.StartWith(Unit.Default)                                  .Throttle(timeout,                    )                                  .Merge(logoutSubject);                  }                public  LogoutManager(TimeSpan  timeout)  :  this(timeout,  Scheduler.Default)    {  }          }    IScheduler  scheduler   scheduler   22  
  • 23. Тестирование  Rx   [Test]     public  void  OnInactivity_Logout()     {          var  testScheduler  =  new  TestScheduler();          var  manager  =  new  LogoutManager(TimeSpan.FromMinutes(5),  testScheduler);          manager.Logout.Subscribe(_  =>  Assert.Pass());          testScheduler.AdvanceBy(TimeSpan.FromMinutes(6).Ticks);          Assert.Fail();                   }   23  
  • 24. Тестирование  Rx   [Test]     public  void  OnActivityWithinThreshold_DoNotLogout()     {          var  testScheduler  =  new  TestScheduler();          var  manager  =  new  LogoutManager(TimeSpan.FromMinutes(5),  testScheduler);          manager.Logout.Subscribe(_  =>  Assert.Fail());          for  (var  i  =  0;  i  <  10;  i++)              { manager.UserActionsObserver.OnNext(Unit.Default);                    testScheduler.AdvanceBy(TimeSpan.FromMinutes(3).Ticks); } }   24  
  • 25. Тестирование  Rx   [Test]     public  void  OnActivityCommand_LogoutImmediately()       {          var  manager  =  new  LogoutManager(TimeSpan.FromMinutes(5));          manager.Logout.Subscribe(_  =>  Assert.Pass());          manager.LogoutCommandsObserver.OnNext(Unit.Default);          Assert.Fail();   }   25  
  • 26. Тестирование  Rx   public  ReactiveCollection(Func<TItem,  string,  bool>  filterFunc) :  this(filterFunc,  NewThreadScheduler.Default,  DispatcherScheduler.Current,                                                  )    {    }            public  ReactiveCollection(Func<TItem,  string,  bool>  filterFunc,  IScheduler  scheduler)            :  this(filterFunc,  scheduler,  scheduler,  scheduler)  {  }      public  ReactiveCollection(Func<TItem,  string,  bool>  filterFunc,          IScheduler  filterScheduler,  IScheduler  observeOn,                                                                          )  {                              var  buffer  =  new  ObservableCollection<TItem>();                            _subscription  =  this.PropertyChangedAsObservable(x  =>  x.Filter) .Throttle(TimeSpan.FromMilliseconds(timeout),  timerScheduler)                                        .Where(_  =>  string.IsNullOrWhiteSpace(Filter)  ||  Filter.Length  >  3)                                        .DistinctUntilChanged(_  =>  Filter)                                        .Merge(this.PropertyChangedAsObservable(x  =>  x.Source))                                        .Do(_  =>  buffer.Clear())                                        .Select(_  =>  Source.EmptyIfNull()                                                            .ToObservable(filterScheduler)                                                            .Where(x  =>  filterFunc(x,  Filter))                                          .Buffer(TimeSpan.FromMilliseconds(200),  timerScheduler)                                                            .Do(x  =>  x.ForEach(buffer.Add)))                                        .Switch()                                        .ObserveOn(observeOn)                                        .Subscribe(_  =>  View  =  buffer.ToArray());  } Scheduler.Default   IScheduler  timerScheduler   26  
  • 27. Тестирование  Rx   [Test] public  void  OnFilter_FilterView() {        var  testScheduler  =  new  TestScheduler();        var  collection  =  new  ReactiveCollection<string>(    (x,  y)  =>  x.Contains(y),  testScheduler)        {                                Source  =  new[]  {"client",  "bad  client",  "item",  "second  item"},        };          collection.Filter  =  "item";          testScheduler.Start();          Assert.IsTrue(new[]  {"item",  "second  item"}.SequenceEqual(collection.View)); }   27  
  • 28. Тестирование  Rx   [Test] public  void  OnFilter_RunFiltrationInAnotherThread() {        var  testScheduler  =  new  TestScheduler();        var  filterThreadId  =  0;        new  ReactiveCollection<int>((x,  y)  =>        {                  filterThreadId  =  Thread.CurrentThread.ManagedThreadId;                  return  true;        },  NewThreadScheduler.Default,  testScheduler,  testScheduler)        collection.Source  =  new  int[10];            testScheduler.Start();        Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId,  filterThreadId); }   28  
  • 29. Тестирование  Rx   [Test] public  void  OnSlowFilter_BufferResults() {        var  testScheduler  =  new  TestScheduler();        var  filterCounter  =  0;        var  collection  =  new  ReactiveCollection<int>((x,  y)  =>        {                if  (++filterCounter%3  ==  0)                        testScheduler.Sleep(TimeSpan.FromMilliseconds(250).Ticks);                return  true;        },  testScheduler)        collection.Source  =  new  int[10];            collection.PropertyChangedAsObservable(x  =>  x.View)                  .Take(3)                  .Subscribe(x  =>  Assert.IsTrue(collection.View.Length%3  ==  0));        testScheduler.Start();        Assert.AreEqual(10,  collection.View.Length); }   29  
  • 30. Тестирование  Rx   [Test] public  void  OnSourceChanged_CallFilter_EvenWithinThreshold() {        var  testScheduler  =  new  TestScheduler();        var  filterCounter  =  0;        var  collection  =  new  ReactiveCollection<int>((x,  y)  =>        {                filterCounter++;                      return  true;        },  testScheduler)          for  (var  i  =  0;  i  <  10;  i++)        {                collection.Source  =  new  int[1];                testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(100).Ticks);        }          Assert.AreEqual(10,  filterCounter); }   30  
  • 31. Тестирование  Rx   [Test] public  void  OnSameFilter_DoNotCallFilter() {        var  testScheduler  =  new  TestScheduler();        var  filterCounter  =  0;        var  collection  =  new  ReactiveCollection<int>((x,  y)  =>        {                filterCounter++;                      return  true;        },  testScheduler)        collection.Source  =  new  int[1];          collection.Filter  =  "firstFilter";          testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(250).Ticks);            filterCounter  =  0;          for  (var  i  =  0;  i  <  10;  i++)        {                collection.Filter  =  "secondFilter";                testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(150).Ticks);                collection.Filter  =  "firstFilter";                testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(250).Ticks);        }        Assert.AreEqual(0,  filterCounter); }   31  
  • 32. Тестирование  Rx  [TestCase(100,  false)] [TestCase(150,  false)] [TestCase(250,  true)] [TestCase(500,  true)] public  void  OnMultipleChangesWithinThreshold_DoNotCallFilter(                                                int  threshold,  bool  runFilter) {        var  testScheduler  =  new  TestScheduler();        var  filterCounter  =  0;        var  collection  =  new  ReactiveCollection<int>((x,  y)  =>        {                filterCounter++;                return  true;        },  testScheduler)  {Source  =  new  int[1]};          testScheduler.AdvanceBy(TimeSpan.FromMinutes(1).Ticks);            filterCounter  =  0;        for  (var  i  =  0;  i  <  10;  i++)        {                collection.Filter  +=  "filter";                testScheduler.AdvanceBy(TimeSpan.FromMilliseconds(threshold).Ticks);        }          if  (runFilter)                  Assert.AreNotEqual(0,  filterCounter);          else                  Assert.AreEqual(0,  filterCounter);   }   32