【问题标题】: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 服务吗?

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

【问题讨论】:

    标签: angular angular2-services


    【解决方案1】:

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

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

    (从https://stackoverflow.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 个实例(每个模块一个)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-17
        • 1970-01-01
        • 1970-01-01
        • 2017-06-10
        • 2014-08-11
        相关资源
        最近更新 更多