web-dev-qa-db-ja.com

サービスがインスタンス化されていることを確認してください

背景

私たちはAngular2アプリを構築しており、1つのモジュールに関連する多くの特定のサービスを蓄積しています。これらのサービスはすべて、アプリのSubject<Type>イベントシステムに疎結合されています。

コンストラクターによるインスタンス化

これらのサービスは直接参照されることはなく、イベントをサブスクライブするだけなので、何らかの方法でインスタンス化する必要があります。現在、使用されている別のサービスのコンストラクターにそれらを挿入するだけです。

// Services not used, just to make sure they're instantiated
constructor(
  private appService1: AppService1,     
  private appService2: AppService2,
  private appService3: AppService3,
  ...
){ }

これはちょっとしたハックのようですが、コンストラクターを介して挿入せずにインスタンス化する必要があるサービスを明示的に記述するためのより良い方法はありますか?

9
Ben Winding

好みに応じてサービスがインスタンス化されることを確認する別のパターンは、モジュールコンストラクターにサービスを注入することです。

このようにして、サービスはモジュールと一緒にインスタンス化されます。

したがって、質問で説明したように別のサービスを挿入するためだけに新しいサービスを作成する代わりに、次のようなことを行うことができます。

_@NgModule ({
    ...
})
export class SomeModule {
    // Services are not used, just to make sure they're instantiated
    constructor(
        private appService1: AppService1,     
        private appService2: AppService2,
        private appService3: AppService3) {
    }
}
_

このアプローチには、useValue: new AppService1()アプローチと比較して少なくとも2つの利点があります。

最初の明白なものは、_AppService1_に依存関係がある場合、それらはAngularのDIによって自動的に解決されます。

2つ目は、アプリのどこからも実際に参照されていないサービスが、グローバル構成サービスの王様です。したがって、このようなサービスのインスタンス化を単一のソースファイルのグローバル構成と組み合わせることができます。これが例です。この場合、これは NgbDatepickerConfig service:です。

_import { NgModule } from "@angular/core";

import {
    NgbDateAdapter, NgbDateNativeUTCAdapter, NgbDateParserFormatter, NgbDatepickerConfig, NgbDatepickerModule,
    NgbDropdownModule, NgbTabsetModule
} from "@ng-bootstrap/ng-bootstrap";

import { UsDateParserFormatter } from "./us-date-parser-formatter";

@NgModule({
    exports: [
        NgbDatepickerModule,
        NgbDropdownModule,
        NgbTabsetModule
    ],
    providers: [
        { provide: NgbDateAdapter, useClass: NgbDateNativeUTCAdapter },
        { provide: NgbDateParserFormatter, useClass: UsDateParserFormatter }
    ]
})
export class NgbImportsModule {
    public constructor(private readonly datepickerConfigService: NgbDatepickerConfig) {
        datepickerConfigService.minDate = { year: 1900, month: 1, day: 1 };
        datepickerConfigService.maxDate = { year: 2099, month: 12, day: 31 };
    }
}
_

この例では、NgbImportsModuleは当初、必要なモジュールをNGBからアプリの残りの部分に再エクスポートするためだけに導入されました。しかし、私のアプリの機能が構築されるにつれて、NgbImportsModuleは単一の場所に変わり、NGBの特定の部分が1つの場所で便利に構成されました。

8

さまざまなコメントで述べられているように、1つのオプションは、単にそのようなサービスを直接インスタンス化することです。これは次のようになります。

// app.module.ts

@NgModule({
  providers: [
    { provide: AppService1, useValue: new AppService1() },
    { provide: AppService2, useValue: new AppService2() },   
    { provide: AppService2, useValue: new AppService3() }
  ]
}) export class AppModule {}

すべてがインジェクターの考え方に反するため、直接のインスタンス化を避けたくなるかもしれませんが、DIやテスト容易性を損なうことはありません。理由の数。

1つの理由は、構成可能なローダーの使用と組み合わせたESモジュールの使用、およびTypeScriptの構造的に型指定された性質の表現力により、SystemJSのようなローダーを活用することにより、実行時にこれらの種類の依存関係でさえテストダブルと交換できることです。

そうは言っても、これveryを頻繁に行う場合は、アプリの構造を再評価する必要があるかもしれませんが、一般的には多くのユースケースがありますこのソリューションが最も簡単です。また、インジェクターのサイクルを壊す可能性があります。

インジェクターのブレークサイクルとは、useValueに指定された式でその値を参照するだけで、別のサービスのコンストラクターに注入する必要がある必要なサービスのインスタンスをキャプチャできることを意味します。 。この手法は、useFactoryを使用するとさらに便利です。とにかく、これはかなり珍しいことですが、便利な回避策になる可能性があります。

2
Aluan Haddad

サービスは、提供するとインスタンス化されます。

コンストラクターで行っているのは、既存のサービスインスタンスをクラスに挿入して、使用できるようにすることです。

サービスをインスタンス化または提供するには、次の3つの方法があります。

1つはアプリモジュールにあります。

@NgModule ({
    providers: [ myservice1, myservice2],
})

これにより、サービスがインスタンス化され、アプリケーション全体で利用できるようになります。

機能モジュールまたは共有モジュールで提供することもでき、同じことを実現します。 appモジュールで提供し、遅延読み込みされた機能モジュールで同じサービスを提供すると、グローバルに利用可能になる2番目のサービスがインスタンス化されます。最初のものはエーテルに消えます。

特定のコンポーネントのサービスをインスタンス化し、コンポーネントごとに1つずつ複数のインスタンス化を行う場合は、コンポーネントのメタデータでサービスを提供します。

@Component ({
     providers: [myservice]
})

次に、コンストラクターを使用してそれを注入します。

0
Derek Kite