首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在角服务中频繁使用BehaviorSubject是一个危险的标志吗?

在角服务中频繁使用BehaviorSubject是一个危险的标志吗?
EN

Stack Overflow用户
提问于 2019-05-24 11:39:27
回答 2查看 959关注 0票数 4

我正在使用pattern编写一个应用程序,并发现自己经常使用这种模式:

代码语言:javascript
复制
@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个或更多这样的公共获取者群体和私人支持主题。这种情况发生得如此频繁,以致于代码感到非常冗长和重复。那么:( a)是否有一种干的方法来做到这一点?( b)我是否滥用了这里的可观测数据?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-05-24 13:59:34

我正在使用pattern编写一个应用程序,并发现自己经常使用这种模式:

您所显示的模式非常类似于状态存储,例如;剩馀NgRXNGXS。不同之处在于,您已经将商店、选择器和还原器放置在一个类中。

在一个地方拥有所有东西都有好处,但是如果每次启动新服务时都必须重写一个新商店,那么这就解释了为什么您说“发生了这么多事情,以至于代码感觉非常冗长和重复”。

这是没有问题的,在互联网上有许多博客文章试图用尽可能少的代码写一个Redux克隆。我的观点是,人们一直在做你所做的事情。

代码语言:javascript
复制
private readonly _widgets: BehaviorSubject<Widget[]> = new BehaviorSubject([]);

上面是州管理器的存储库。这是一个可以观察到的状态,包含当前状态,并发出对该状态的更改。

代码语言:javascript
复制
public get widgets() { return this._widgets.value; }

上面是状态管理器的快照。这允许您在存储上进行特定的计算,而不必订阅,但与其他状态存储一样,使用快照可能会出现争用条件问题。您也不应该从模板直接访问它,因为它将触发“表达式检查后已更改”错误。

代码语言:javascript
复制
public readonly widgets$ = this._widgets.asObservable();

以上是商店的选择器。商店通常有许多选择器,这些选择器允许应用程序的不同部分侦听有关特定主题的存储更改。

代码语言:javascript
复制
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]);
}

我们在州立商店的图书馆里没有上面的东西。上面的动作和减速器被分为两部分。该操作通常包含有效负载(在您的示例中是一个小部件),还原器执行修改存储的工作。

当我们使用动作和还原器时,它将存储的业务逻辑与读取当前状态、更新状态和保存下一个状态的问题分离开来。你的例子很简单。在需要订阅、修改和发出更改的大型应用程序中,当您只想切换一个布尔标志时,这些更改可能会成为开销样板代码。

许多服务将有3-5个或更多这样的公共获取者群体和私人支持主题。这种情况发生得如此频繁,以致于代码感到非常冗长和重复。

你正在进入重新发明车轮的领域。

在我看来,你有两个可能的选择。创建您自己的状态存储框架,让您感到更舒服,或者使用我上面列出的库中的一个现有的状态存储。我们不能告诉你该走哪条路,但是我做过很多有角度的项目,我可以告诉你没有正确的答案。

真正让源代码感觉不那么冗长和重复的是高度的固执己见。让它变得不那么冗长的事情有一天可能会以设计错误的形式困扰您,重复的源代码是痛苦的,但总有一天,您会感激您可以修改一行代码,而不会影响到源代码的其他领域。

( a)是否有一种干的方法来做到这一点,以及

使源代码干涸的唯一方法是将状态管理的实现与业务逻辑分离开来。这就是我们讨论如何为状态商店提供一个良好的设计模式的地方。

  • 你用选择器吗?
  • 你用行动吗?
  • 你用减速机吗?

您希望这些东西在哪里(在它们自己的文件或服务的方法中)?您想如何命名它们,应该重用它们还是为每一种边缘情况创建新的?

很多问题都是个人的选择。

我可以使用NGXS作为示例重写您的示例,但是您可能不会觉得这很枯燥,因为框架需要复杂才能有用。我能告诉您的是,当您需要做一些您以前没有做过的事情时,更容易阅读NGXS的文档,然后尝试自己发明它,并冒着出错的风险。这并不意味着NGXS总是正确的,但至少您可以抱怨这不是您的错:)

代码语言:javascript
复制
@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()));
    }
}

( b)我在这里误用了可观察到的东西吗?

绝对不能滥用可观察到的东西。您很好地理解了为什么服务应该是无状态和反应性的。我认为你只是在自己发现州立商店的价值,现在你正在寻找更容易使用它们的方法。

票数 7
EN

Stack Overflow用户

发布于 2019-05-24 11:55:37

( A)为了避免创建BehaviorSubject的重复代码,您可以创建包含键和值的BehaviorSubject,我们通过使用键进行订阅,因此现在不需要每次想要使用时都创建BehaviorSubject

服务

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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);
    })
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56291934

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档