1. 程式人生 > >依賴注入Dependency Injection(一)

依賴注入Dependency Injection(一)

Let's say we have a CustomerEditScreen that needs to load a Customer entity by ID from a web service. We wouldn't want to place all the details of our AJAX implementation inside our CustomerEditScreen class. Instead, we would want to factor that into a CustomerService class that our CustomerEditScreen

, or any other class, can use when it needs to load a Customer. Aurelia's dependency injection container lets you accomplish this by declaring that the CustomerEditScreen needs to have a CustomerService injected at creation time.

我們不想把Ajax請求寫在CustomerEditScreen裡面,我們把Ajax請求放在CustomerService裡面,這樣任何class都可以利用這個檔案請求。Aurelia's dependency injection container 通過在建立時候宣告CustomerEditScreen需要CustomerService

 注入來幫你完成。

Typically, you would use Decorators, an ES Next feature supported by both Babel and TypeScript. Here's what it looks like to declare that the CustomerEditScreen needs a CustomerService

用修飾符,ES6特性,Babel和TS都支援。下面是個demo。

import {CustomerService} from 'backend/customer-service';
import {inject} from 'aurelia-framework';

@inject(CustomerService)
export class CustomerEditScreen {
  constructor(private customerService: CustomerService) {
    this.customer = null;
  }

  activate(params) {
    return this.customerService.getCustomerById(params.customerId)
      .then(customer => this.customer = customer);
  }
}


Notice that we use the inject decorator and that the constructor signature matches the list of dependencies in the inject decorator. This tells the DI that any time it wants to create an instance of CustomerEditScreen it must first obtain an instance of CustomerService which it can inject into the constructor of CustomerEditScreen during instantiation. You can have as many injected dependencies as you need. Simply ensure that the inject decorator and the constructor match one another. 

我們用inject修飾符,建構函式匹配inject裡面列出的依賴。注意,建構函式裡面的依賴必須要一一對應inject修飾符裡裡面的。這個告訴DI,它任何時候想要建立CustomerEditScreen例項,必須首先獲得CustomerService例項,這個要獲得的例項可以在CustomerEditScreen例項化的時候被注入到CustomerEditScreen的建構函式中。

多個依賴的注入需要建構函式裡面的依賴和inject裡面的依賴一一對應。

import {CustomerService} from 'backend/customer-service';
import {CommonDialogs} from 'resources/dialogs/common-dialogs';
import {EventAggregator} from 'aurelia-event-aggregator';
import {inject} from 'aurelia-framework';

@inject(CustomerService, CommonDialogs, EventAggregator)
export class CustomerEditScreen {
  constructor(private customerService: CustomerService, private dialogs: CommonDialogs, private ea: EventAggregator) {
    this.customer = null;
  }

  activate(params) {
    return this.customerService.getCustomerById(params.customerId)
      .then(customer => this.customer = customer)
      .then(customer => this.ea.publish('edit', customer));
  }
}


If you are using TypeScript, you can take advantage of an experimental feature of the language to have the TypeScript transpiler automatically provide Type information to Aurelia's DI. You can do this by configuring the TypeScript compiler with the "emitDecoratorMetadata": true option in the compilerOptions section of your tsconfig.json file. If you do this, you don't need to duplicate the type information with inject, instead, as long as your constructor definition contains its paramaters' types, you can use Aurelia's autoinject decorator like this

如果用的是ts,那麼只需要進行上述的配置就可以自動注入依賴。inject換成了autoinject。

Interestingly, you don't need to use our autoinject decorator at all to get the above to work. The TypeScript compiler will emit the type metadata if any decorator is added to the class. Aurelia can read this metadata regardless of what decorator triggers TypeScript to add it. We simply provide the autoinject decorator for consistency and clarity.

其實如果用ts,連autoinject都不需要新增,之所以新增,只是為了讓程式碼更清晰。

import {CustomerService} from 'backend/customer-service';
import {CommonDialogs} from 'resources/dialogs/common-dialogs';
import {EventAggregator} from 'aurelia-event-aggregator';
import {autoinject} from 'aurelia-framework';

@autoinject
export class CustomerEditScreen {
  constructor(private customerService: CustomerService, private dialogs: CommonDialogs, private ea: EventAggregator) {
    this.customer = null;
  }

  activate(params) {
    return this.customerService.getCustomerById(params.customerId)
      .then(customer => this.customer = customer)
      .then(customer => this.ea.publish('edit', customer));
  }
}


If you aren't using Babel's or TypeScript's decorator support (or don't want to), you can easily provide inject metadata using a simple static method or property on your class:

如果babel和ts都不用,那麼就這樣新增依賴。用一個inject靜態方法。

import {CustomerService} from 'backend/customer-service';
import {CommonDialogs} from 'resources/dialogs/common-dialogs';
import {EventAggregator} from 'aurelia-event-aggregator';

export class CustomerEditScreen {
  static inject() { return [CustomerService, CommonDialogs, EventAggregator]; }

  constructor(customerService, dialogs, ea) {
    this.customerService = customerService;
    this.dialogs = dialogs;
    this.ea = ea;
    this.customer = null;
  }

  activate(params) {
    return this.customerService.getCustomerById(params.customerId)
      .then(customer => this.customer = customer)
      .then(customer => this.ea.publish('edit:begin', customer));
  }
}


因為我們class檔案裡面的方法都是public或者private,我們想要使用必須對class進行例項化,只有static可以直接使用,

所以需要注入依賴例項化後才能呼叫。