使用同一服务的多个实例

Posted

技术标签:

【中文标题】使用同一服务的多个实例【英文标题】:Using multiple instances of the same service 【发布时间】:2016-11-23 18:07:09 【问题描述】:

我有一个服务,像这样:

@Injectable()
export class EditorService  ... 

我有一个组件,像这样:

@Component(
  ...
  template: `<child-component></child-component>`,
  providers: [EditorService],
  ...
)
export class SomeComponent 
  constructor(
    private _appleEditorService: EditorService,
    private _pearEditorService: EditorService) 
  

你可能已经注意到,这个组件有一个子组件:

@Component(
  ...
  selector: 'child-component',
  ...
)
export class ChildComponent 
  constructor(
    private _appleEditorService: EditorService,
    private _pearEditorService: EditorService) 
  

如您所见,我想要我的EditorService 的两个实例:一个用于编辑苹果,一个用于编辑梨。但是,上面的代码不起作用,因为 Angular 无法知道 EditorService 的哪个实例是哪个实例,因为它们的变量名是私有的。 _pearEditorService in ChildComponent 可能与 _appleEditorServiceSomeComponent 中引用相同的实例。

问题:否则,我可以两次使用同一个 Angular2 服务吗?

编辑:我的问题是是否可以使用同一个类。我知道有一些解决方法,可以为服务的每个实例创建一个单独的类,甚至可以通过继承使用很少的代码来实现。我只是想知道是否可以不这样做。

【问题讨论】:

【参考方案1】:

Angular DI 为每个提供者维护一个实例。在您的情况下,如果您有两个具有相同类型的构造函数参数,它们会解析为同一个实例。

您可以做的是提供一个工厂函数(与简单的useFactory 提供程序事件不同,尽管它也使用它)

(从https://***.com/a/37517575/217408复制的示例)

 provide: EditorService, useFactory: 
    (dep1, dep2) => 
        return (x) =>  
            new EditorService(x, dep1, dep2);
        
    , deps: [Dep1, Dep2]
)

....

constructor(@Inject(EditorService) editorServiceFactory: any) 
  let editorService1 = editorServiceFactory(1);
  let editorService2 = editorServiceFactory(2);

如果您有固定数量的实例,这应该可以工作:

 provide: 'instance1', useClass: EditorService ,
 provide: 'instance2', useClass: EditorService ,
export class SomeComponent 
    constructor(
        @Inject('instance1') private _appleEditorService: EditorService,
        @Inject('instance2') private _pearEditorService: EditorService) 

【讨论】:

^ 这实际上可能是一个可行的解决方法。以前没见过,现在无法测试。会给你投票,因为这是对 OP 的更直接的答案,而不是我的答案:-) 指出问题的其他观点总是一个好主意,也可以投票:-) 我终于明白什么时候我使用注入和一个简单的new() - 我已经创建了一个plunker,所以我会一直记住它,以防其他人想要:plnkr.co/edit/tsay1qciOkxOS74MOBNo?p=info 我了解linked answer 中的解决方案如何与嵌套在(dep1, dep2) 内的函数(x) 一起工作,但我不明白这里的预期是什么,因为(x) 是@987654333 周围的闭包@。在这个答案中嵌套的函数是否错误?如果这个答案是正确的,那么如果嵌套的 deps 没有被输入,它们如何得到正确的解析? 你是对的。非常感谢您的提示。我混合了嵌套方法的签名。 plnkr.co/edit/Jbrr0vfMxb1sIR2GvVtW?p=preview【参考方案2】:

也许会重新考虑你的设计

我认为你可能不得不重新考虑你的设计。让两个变量指向完全相同的 EditorService 有什么意义?如果他们共享相同的实现。

继承解决方案

对此的解决方案可能是使用继承。我没有看到你的服务,所以我不确定他们做了什么,但从这段代码中我假设“苹果”功能和“梨”功能实际上是不同的。那么这表明您可能有一些设计问题,并且您的 EditorService 可能做得太多。也许您可以将与 Apples 和 Pears 相似的代码移动到名为 EditorService 的服务中,然后让两个类扩展它。一个AppleEditorService和一个PearEditorService

使用通用fruitService

只是想多谈谈我认为您可能需要重新考虑您的设计的原因。让我们假设 appels 和 pears 实际上具有相同的功能。所以EditorService也一样。您希望一个变量用于“Apples”,一个用于“Pears”。如果我看到该代码(假设其他人在您的团队中工作),并且我注意到两个变量都指向同一个服务。 (服务是单例的),我只是想删除它并制作类似fruitService : EditorService或类似的东西。

无论哪种方式,您的问题都让我认为应该更改设计。您不想要服务的两个实例,服务是单例的,虽然您可以找到解决方法,但我认为这并不是解决问题的真正方法。


但正如我之前提到的。想想你的设计。你真的需要两个子服务,或者你能用不同的方式解决它吗?你能吃苹果和梨子类水果吗?您可以只使用一个变量“fruitService”而不是两个苹果/梨吗?当您将来也想存储“草莓”时会发生什么?有一堆变量指的是同一个服务(因为它们做同样的事情)但具有不同的变量名称​​不是一个好主意。想象一下在代码中看到这一点

 appleService = EditorService;
 pearService = EditorService;
 starwberryService = EditorService;
 lemonService = EditorService;
 ...
 guavaService = EditorService;

这不是表示代码有些奇怪吗?我可以想象(此时您没有提供代码)您存储了一个水果实例数组。但是也许fruitService 可以工作,然后存储fruitService.store(Apple a) 并将其放入“水果”数组中。使用“过滤器”从阵列中获取正确的水果应该不会太难。

^ 这只是在没有更多代码的情况下编写的。如果您编辑您的问题,有些事情可能不再成立。

【讨论】:

好吧,我可以想到 许多 原因,为什么任何 any 可变类会被实例化多次,即使它是用零参数构造的。这就是实例的重点,如果你改变一个,改变它的状态,你不会改变另一个。继承显然是最快的解决方法,感谢您指出这一点,但我的问题真的是 Angular2 是否可以用同一个类做到这一点。 @Devaro 你在这里使用服务。服务是单例的。我知道您可以获得它们的多个实例 - 在 不同的 类中,但不能在同一个类中。 (范围提供者)。单例总是只有一个实例。我不认为继承在这里是一种解决方法——我认为这只是处理服务的一种正常方式。但是您的服务可能不好,无法从您的代码中推断出来。也许他们没有以 angular2 的最佳方式做事。 嗯好的。没有压倒他们是单身人士的事实。好的,谢谢你的帮助。 这不应该是公认的答案,因为它不能正确回答问题。有关解决方案,请参阅下面的 Gunters 回答【参考方案3】:

如何使用服务中的对象来存储 X 个实例...

export class Test 

  private vars: any;

  constructor() 
    vars = ;
  

  createInstance(var: any) 
    this.vars[var] = var;
  

  doSomething(var: any, options): void 
    this.vars[var] = someChangeToVar;
  

  doSomethingElse(var: any): void 
    this.vars[var] = someOtherChangeToVar;
  

  get var(var: any): any 
    return this.vars[var];
  


你调用它时第一个参数是实例的名称...

export class SomeComponent 

  constructor(private test: Test) 

    // create instance
    this.test.createInstance('myInstance');

    // do something to it
    this.test.doSomething('myInstance', 'some input here');

    // get it back
    const result = this.test.get('myInstance');

    console.log(result);
  
...

【讨论】:

【参考方案4】:

不理想,但如果您为每个组件创建一个模块,然后在每个模块上导入您的服务,您将拥有相同服务的 2 个实例(每个模块一个)。

【讨论】:

以上是关于使用同一服务的多个实例的主要内容,如果未能解决你的问题,请参考以下文章

多个服务可以使用同一个 Zookeeper 实例吗?

Angular 8:如何为同一组件的多个实例提供不同的服务实例

在服务器上安装同一 Windows 服务的多个实例

如何在同一台机器上安装多个MySQL的实例(转)

是否可以在单个 GRPC 服务器上运行同一服务的多个实例?

使用Inno Setup安装具有不同配置的同一Windows服务的多个实例