Является ли частое использование BehaviorSubject в сервисах Angular красным флагом? - PullRequest
2 голосов
/ 24 мая 2019

Я пишу приложение с использованием Angular и постоянно использую этот шаблон:

@Injectable(...)
export class WidgetRegsitryService {
  private readonly _widgets: BehaviorSubject<Widget[]> = new BehaviorSubject([]);
  public get widgets() { return this._widgets.value; }
  public readonly widgets$ = this._widgets.asObservable();

  public add(widget: Widget) {
    const old = this._widgets.value.slice();
    old.push(widget);
    this._widgets.next(old);
  }
}

Многие службы будут иметь 3-5 или более таких групп публичных добытчиков и частных субъектов поддержки. Случается так, что код кажется очень многословным и повторяющимся. Итак: а) есть ли СУХОЙ способ сделать это, и б) я неправильно использую Наблюдаемые здесь?

Ответы [ 2 ]

3 голосов
/ 24 мая 2019

Я пишу приложение с использованием Angular и постоянно использую этот шаблон:

Показанный вами шаблон очень похож на хранилище состояний такие как; Redux , NgRX или NGXS .Разница в том, что вы поместили магазин, селекторы и редукторы в один класс.

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

В этом нет ничего плохого, и в Интернете есть много постов в блоге.которые пытаются написать клон Redux, используя как можно меньше строк кода.Я хочу сказать, что люди все время делают именно то, что вы делаете.

private readonly _widgets: BehaviorSubject<Widget[]> = new BehaviorSubject([]);

Выше store менеджера состояний.Это наблюдаемая, которая содержит текущее состояние и передает изменения в это состояние.

public get widgets() { return this._widgets.value; }

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

public readonly widgets$ = this._widgets.asObservable();

Выше селектор для магазина,Хранилища часто имеют много селекторов, которые позволяют различным частям приложения прослушивать изменения хранилища по определенным темам.

public add(widget: Widget) {
   const old = this._widgets.value.slice();
   old.push(widget);
   this._widgets.next(old);
   // above can be rewritten as
   this._widgets.next([...this.widgets, widget]);
}

В библиотеках state store выше нет.Вышеуказанное разделено на две части: действие и редуктор .Действие часто содержит полезную нагрузку (в вашем случае - виджет), а редуктор выполняет работу по изменению хранилища.

Когда мы используем действия и редукторы, он разделяет бизнес-логику того, какХранение должно измениться от проблем чтения текущего состояния, обновления состояния и сохранения следующего состояния.Пока твой пример очень прост.В больших приложениях, требующих подписки, изменения и выдачи изменений, они могут стать стандартным кодом, когда все, что вам нужно сделать, это переключить логический флаг.

У многих служб будет 3-5 или болеетакие группы государственных добытчиков и частных субъектов поддержки.Случается так, что код кажется очень многословным и повторяющимся.

Вы вступаете в сферу нового изобретения колеса.

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

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

a) существует ли СУХОЙ способ сделать это, и

Единственный способ высушить исходный код - этоотделить реализацию state management от бизнес-логики .Здесь мы обсуждаем, как создать хороший шаблон проектирования для государственного хранилища .

  • Используете ли вы селекторы?
  • Используете ли вы действия?
  • Используете ли вы редукторы?

Где вы хотите, чтобы эти вещи были (в своих собственных файлах или методах службы?).Как вы хотите назвать их, и следует ли вам повторно использовать их или создавать новые для каждого крайнего случая?

Это много вопросов, которые действительно являются личным выбором.

Я могу переписать ваш пример, используя NGXS в качестве примера, но это может показаться вам dry , потому что фреймворки должны быть сложными, чтобы быть полезными. Что я могу вам сказать, так это то, что вам легче читать документацию по NGXS, когда вам нужно сделать что-то, чего вы раньше не делали, а затем попытаться придумать это самостоятельно и рискнуть ошибиться. Это не значит, что NGXS всегда прав, но, по крайней мере, вы можете жаловаться, что это не ваша вина:)

@State<Widget[]>({
    name: 'widgets',
    defaults: []
})
export class WidgetState {
    @Action(AddWidgetAction)
    public add(ctx: StateContext<Widget[]>, {payload}: AddWidgetAction) {
        ctx.setState([...ctx.getState(), payload]);
    }
}

@Component({...})
export class WidgetsComponent {
    @Select(WidgetState)
    public widgets$: Observable<Widget[]>;

    public constructor(private _store: Store) {};

    public clickAddWidget() {
        this._store.dispatch(new AddWidgetAction(new Widget()));
    }
}

б) Я неправильно использую Наблюдаемые здесь?

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

0 голосов
/ 24 мая 2019

A) Чтобы избежать повторяющегося кода для создания BehaviorSubject, вы можете создать BehaviorSubject, который содержит ключ и значение, и мы подписываемся, используя ключ, поэтому теперь нет необходимости создавать BehaviorSubject каждый раз, когдамы хотим использовать.

Сервис

interface Event {
  key: string;
  value: any;
}


@Injectable({
  providedIn: 'root'
})

export class Broadcaster {

  // subject 
  protected _eventsSubject = new BehaviorSubject<Event>();
  constructor() {
  }

   broadcast(key: any, value: any) {
    this._eventsSubject.next({ key, value }); // here we are setting the key and value of our subject
   }

  on<T>(key: any): Observable<T> {
    return this._eventsSubject.asObservable()
            .pipe(
                filter(e => e.key === key),
                map(e => e.value)
            );
  }
}

componentOne

import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {

// here we are sending a data and setting a key of subject to 'msg1'

this.broadcaster.broadcast('msg1', 'data of msg1');

}

componentTwo

import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {

// here we are sending a data and setting a key of subject to 'msg2'

this.broadcaster.broadcast('msg2', 'data of msg2');
}

componentThree

import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {
// here we subscribe our subject and getting a value of msg1 key
    this.broadcaster.on('msg1').subscribe(resp => {
      console.log(resp);
    })
}
...