基础知识-依赖注入 一审完毕

This commit is contained in:
Zhicheng Wang 2016-05-16 19:47:52 +08:00
parent 68631c58f5
commit 8e84ec2c15
1 changed files with 91 additions and 92 deletions

View File

@ -7,9 +7,9 @@ include ../_util-fns
we really can't build an Angular application without it.
It's used so widely that almost everyone just calls it _DI_.
**依赖注入** 是一个很重要的程序设计模式。
**依赖注入**是一个很重要的程序设计模式。
Angular有自己的依赖注入框架离开了它我们几乎没法构建Angular应用。
它使用得非常广泛,以至于几乎每个人都会把它简称为 _DI_
它使用得非常广泛以至于几乎每个人都会把它简称为_DI_。
In this chapter we'll learn what DI is and why we want it.
Then we'll learn [how to use it](#angular-di) in an Angular app.
@ -45,14 +45,14 @@ include ../_util-fns
The problem is that our `Car` class is brittle, inflexible, and hard to test.
问题,我们这个`Car`类过于脆弱、缺乏弹性并且难以测试。
问题在于,我们这个`Car`类过于脆弱、缺乏弹性并且难以测试。
Our `Car` needs an engine and tires. Instead of asking for them,
the `Car` constructor creates its own copies by "new-ing" them from
the very specific classes, `Engine` and `Tires`.
我们的汽车(`Car`)类需要一个引擎(`Engine`)和轮胎(`Tires`),它没有去请求一个现成的实例,
而是在构造函数中用具体的类`Engine`和`Tires`新创建了一份只自己用的副本。
而是在构造函数中用具体的类`Engine`和`Tires`新创建了一份只自己用的副本。
What if the `Engine` class evolves and its constructor requires a parameter?
Our `Car` is broken and stays broken until we rewrite it along the lines of
@ -65,7 +65,7 @@ include ../_util-fns
如果`Engine`类升级了,并且它的构造函数要求传入一个参数了怎么办?
我们这个`Car`类就被破坏了,并且直到我们把创建代码重写为`#{prefix}engine = new Engine(theNewParameter)`之前,它都坏着。
当我们首次写`Car`类时,我们不会在乎`Engine`构造函数的参数。其实现在我们也不想在乎。
当我们首次写`Car`类时,我们不会在乎`Engine`构造函数的参数。就算现在我们也不想在乎。
但当`Engine`类的定义发生变化时,我们就不得不在乎了,`Car`类也不得不跟着改变。
这就会让`Car`类过于脆弱。
@ -101,14 +101,14 @@ include ../_util-fns
How do we confirm that it actually does flash a warning
if we can't swap in low-pressure tires during the test?
如果我们的`Car`应该在轮胎气压低的时候闪一个警示灯该怎么办?
如果我们没法在测试期间换上一个低气压的轮胎,我们该如何确认它确实能正确的闪警示灯?
如果我们的`Car`应该在轮胎气压低的时候闪一个警示灯该怎么办?
如果我们没法在测试期间换上一个低气压的轮胎,我们该如何确认它能正确的闪警示灯?
We have no control over the car's hidden dependencies.
When we can't control the dependencies, a class becomes difficult to test.
我们没法控制这辆车背后隐藏的依赖。
而如果我们不能控制这些依赖,类就会变得难于测试。
而如果不能控制这些依赖,类就会变得难以测试。
How can we make `Car` more robust, flexible, and testable?
@ -132,14 +132,14 @@ include ../_util-fns
发生了什么?我们把依赖的定义移到了构造函数中。
我们的`Car`类不再创建引擎或者轮胎。
它仅仅“消费”它们。(译注:指直接使用成品)
它仅仅“消费”它们。
// #enddocregion why-3-1
// TypeScript only
.l-sub-section
:marked
We also leverage TypeScript's constructor syntax for declaring parameters and properties simultaneously.
我们还借助了TypeScript的构造语法来同时定义参数和属性。
我们又一次借助TypeScript的构造器语法来同时定义参数和属性。
// #docregion why-3-2
:marked
Now we create a car by passing the engine and tires to the constructor.
@ -156,12 +156,12 @@ include ../_util-fns
We can pass in any kind of engine or tires we like, as long as they
conform to the general API requirements of an engine or tires.
酷!引擎和轮胎这两个依赖的定义从`Car`类本身解耦开了。
只要喜欢,我们就可以传入任何类型的引擎或轮胎,只要它们能满足引擎或轮胎的普遍API需求。
酷!引擎和轮胎这两个依赖的定义从`Car`类本身解耦开了。
只要喜欢,我们就可以传入任何类型的引擎或轮胎,只要它们能满足引擎或轮胎的通用API需求。
If someone extends the `Engine` class, that is not `Car`'s problem.
如果有人扩展了`Engine`类,那就不再是`Car`类的烦恼。
如果有人扩展了`Engine`类,那就不再是`Car`类的烦恼
// #enddocregion why-4
// Must copy the following, due to indented +make.
.l-sub-section
@ -169,7 +169,7 @@ include ../_util-fns
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
something like this:
`Car`的 _消费者_ 也有这个问题。消费者必须更新创建这辆车的代码,就像这样:
`Car`的_消费者_也有这个问题。消费者必须更新创建这辆车的代码就像这样
- var stylePattern = { otl: /(new Car.*$)/gm };
+makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-param', '', stylePattern)(format=".")
@ -200,7 +200,7 @@ include ../_util-fns
It's a coding pattern in which a class receives its dependencies from external
sources rather than creating them itself.
它是一编程模式,该模式可以让一个类从外部源中获得它的依赖,而不必自创建它们。
它是一编程模式,该模式可以让一个类从外部源中获得它的依赖,而不必自创建它们。
Cool! But what about that poor consumer?
Anyone who wants a `Car` must now
@ -227,20 +227,20 @@ include ../_util-fns
现在只需要三个创建方法,这还不算太坏。
但是当应用规模变大之后,维护它将变得惊险重重。
这个工厂类将变成一个由相互依赖的工厂方法构成的矩形蜘蛛网。
这个工厂类将变成一个由相互依赖的工厂方法构成的巨型蜘蛛网。
Wouldn't it be nice if we could simply list the things we want to build without
having to define which dependency gets injected into what?
如果我们能简单的列出我们想建造的东西,而不用定义要把哪些依赖注入哪些对象,是不是会很美妙?
如果我们能简单的列出我们想建造的东西,而不用定义要把哪些依赖注入哪些对象,那该多好!
This is where the dependency injection framework comes into play.
Imagine the framework had something called an _injector_.
We register some classes with this injector, and it figures out how to create them.
到了让依赖注入框架一展身手的时候了!
想象框架中有一个叫做 _注入器injector_ 的东西。
我们使用这个注入器注册一些类,会指出该如何创建它们。
想象框架中有一个叫做_注入器Injector_的东西。
我们使用这个注入器注册一些类,会指出该如何创建它们。
When we need a `Car`, we simply ask the injector to get it for us and we're good to go.
@ -257,11 +257,11 @@ include ../_util-fns
多方皆赢的方式。`Car`不需要知道如何创建`Engine`和`Tires`的任何事。
消费者不知道如何创建`Car`的任何事。
我们不需要一个巨大的工厂类来维护它们。
`Car`和消费者只要简单的说出它们想要什么,注入器就会交给它们。
`Car`和消费者只要简单的说出它们想要什么,注入器就会交给它们。
This is what a **dependency injection framework** is all about.
这就是关于 **依赖注入框架** 的一切
这就是“什么是**依赖注入框架**”问题的答案
Now that we know what dependency injection is and appreciate its benefits,
let's see how it is implemented in Angular.
@ -278,7 +278,7 @@ include ../_util-fns
Angular ships with its own dependency injection framework. This framework can also be used
as a standalone module by other applications and frameworks.
Angular自带了它自己的依赖注入框架。此框架也能被当做独立模块用于其它应用和框架中。
Angular自带了它自己的依赖注入框架。此框架也能被当做独立模块用于其它应用和框架中。
That sounds nice. What does it do for us when building components in Angular?
Let's see, one step at a time.
@ -289,7 +289,7 @@ include ../_util-fns
We'll begin with a simplified version of the `HeroesComponent`
that we built in the [The Tour of Heroes](../tutorial/).
我们从在[英雄指南](../tutorial/)中构建过的`HeroesComponent`的一个简化版本开始。
我们从当初在[英雄指南](../tutorial/)中构建过的`HeroesComponent`的一个简化版本开始。
// #enddocregion di-1
+makeTabs(
`dependency-injection/ts/app/heroes/heroes.component.1.ts,
@ -308,7 +308,7 @@ include ../_util-fns
Our stripped down version has only one child, `HeroListComponent`,
which displays a list of heroes.
`HeroesComponent`是 *英雄* 特性分区中的根组件。它管理着本分区的所有子组件。
`HeroesComponent`是*英雄*特性分区中的根组件。它管理着本分区的所有子组件。
我们简化后的版本只有一个子组件`HeroListComponent`,它显示一个英雄列表。
// #enddocregion di-2
// #docregion di-3
@ -322,11 +322,11 @@ include ../_util-fns
现在`HeroListComponent`从`HEROES`获得英雄数据,一个在另一个文件中定义的内存数据集。
它在开发的早期阶段可能还够用,但离完美就差得远了。
一旦我们开始测试此组件,或者想从远端服务器获得英雄数据,我们不得不修改`heroes`的实现,并要修改每个用到了`HEROES`模拟数据的地方。
一旦我们开始测试此组件,或者想从远端服务器获得英雄数据,我们不得不修改`heroes`的实现,并要修改每个用到了`HEROES`模拟数据的地方。
Let's make a service that hides how we get hero data.
我们来制作一个服务,把获取英雄数据的代码封装起来。
我们来制作一个服务,把获取英雄数据的代码封装起来。
// #enddocregion di-3
// Unnecessary for Dart
@ -341,7 +341,7 @@ include ../_util-fns
Our `HeroService` exposes a `getHeroes` method that returns
the same mock data as before, but none of its consumers need to know that.
我们的`HeroService`暴露了`getHeroes`方法,用于返回跟以前一样的模拟数据,但是它的消费者不需要了解这一点。
我们的`HeroService`暴露了`getHeroes`方法,用于返回跟以前一样的模拟数据,但它的消费者不需要知道这一点。
// #enddocregion di-4
// #docregion di-5
.l-sub-section
@ -370,13 +370,12 @@ include ../_util-fns
### Configuring the injector
### 配置注入器
<a id="bootstrap"></a>
We don't have to create an Angular injector.
Angular creates an application-wide injector for us during the bootstrap process.
<a id="bootstrap"></a>
我们并不需要自己创建一个Angular注入器。
Angular在启动期间会自动为我们创建一个应用级注入器。
Angular在启动期间会自动为我们创建一个应用级注入器。
// #enddocregion di-configure-injector-1
+makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (节选)')(format='.')
// #docregion di-configure-injector-2
@ -386,9 +385,9 @@ include ../_util-fns
We'll explain what [providers](#providers) are later in this chapter.
Before we do, let's see an example of provider registration during bootstrapping:
我们必须先注册**供应商**来配置好注入器,这在创建应用所需的服务时会用到。
我们必须先注册**供应商Provider**来配置好注入器,这在创建应用所需的服务时会用到。
我们将在本章的稍后部分解释什么是[供应商](#providers)。
在此之前,我们先来看一个启动期间注册供应商的例子。
在此之前,我们先来看一个启动期间注册供应商的例子。
// #enddocregion di-configure-injector-2
+makeExample('dependency-injection/ts/app/main.1.ts', 'bootstrap')(format='.')
// #docregion di-configure-injector-3
@ -412,7 +411,7 @@ include ../_util-fns
Because the `HeroService` is used within the *Heroes* feature area &mdash;
and nowhere else &mdash; the ideal place to register it is in the top-level `HeroesComponent`.
首选的方式是在应用组件中注册应用级的供应商。
首选的方式是在应用组件中注册供应商。
因为`HeroService`是用于*英雄*功能区的 —— 并且没别处用它 —— 所以注册它的理想地点就是`HeroesComponent`的顶层。
// #enddocregion di-configure-injector-3
// #docregion di-register-providers-1
@ -516,9 +515,9 @@ include ../_util-fns
If we let Angular do its job, we'll enjoy the benefits of automated dependency injection.
但无论在《英雄指南》还是其它范例中,我们都没有发现这样的代码。
在必要时,我们 *可以* 写[使用显式注入器的代码](#explicit-injector),但却很少这样做。
在必要时,我们*可以*写[使用显式注入器的代码](#explicit-injector),但却很少这样做。
当Angular为我们创建组件时 —— 无论通过像`<hero-list></hero-list>`这样的HTML标签还是通过[路由](./router.html)导航到组件 —— 它都会自己管理好注入器的创建和调用。
只要让Angular做好它自己的工作我们就能安心享受自动依赖注入带来的好处。
只要让Angular做好它自己的工作我们就能安心享受自动依赖注入带来的好处。
// #enddocregion di-create-injector-implicitly-2
// #docregion di-singleton-services
:marked
@ -561,7 +560,7 @@ include ../_util-fns
:marked
Learn more in [Testing](../testing/index.html).
学习更多知识,参见[测试](../testing/index.html)。
学习更多知识,参见[测试](../testing/index.html)。
// #enddocregion di-testing-component-2
// #docregion di-service-service-1
:marked
@ -576,7 +575,7 @@ include ../_util-fns
adding a constructor that takes a `Logger` parameter.
如果它有依赖呢?如果它需要通过一个日志服务来汇报自己的活动呢?
我们同样用 *构造函数注入* 模式,来添加一个带有`Logger`参数的构造函数。
我们同样用*构造函数注入*模式,来添加一个带有`Logger`参数的构造函数。
Here is the revision compared to the original.
@ -593,7 +592,7 @@ include ../_util-fns
The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `_logger`.
We call that property within our `getHeroes` method when anyone asks for heroes.
这个构造函数现在会要求一个`Logger`类的实例注入进来,并且把它存在一个名叫`_logger`的私有属性中。
现在,这个构造函数会要求一个`Logger`类的实例注入进来,并且把它存到一个名为`_logger`的私有属性中。
当别人要求获得英雄数据时,我们会在`getHeroes`方法中使用这个属性。
// #enddocregion di-service-service-2
// #docregion di-injectable-1
@ -605,7 +604,7 @@ include ../_util-fns
:marked
<a id="injectable"></a>
### Why @Injectable?
### 为什么要@Injectable
### 为什么要@Injectable
Notice the `@Injectable()` #{decoration} above the service class.
We haven't seen `@Injectable()` before.
As it happens, we could have added it to our first version of `HeroService`.
@ -618,7 +617,7 @@ include ../_util-fns
We need it now... now that our service has an injected dependency.
We need it because Angular requires constructor parameter metadata in order to inject a `Logger`. !{tsmetadata}
现在,我们需要了 …… 现在,该服务有了一个需要被注入的依赖。
现在,我们需要了…… 现在,该服务有了一个需要被注入的依赖。
之所以需要它是因为Angular需要关于构造函数参数的元数据有了元数据才能注入`Logger`。!{tsmetadataCn}
// #enddocregion di-injectable-1
@ -629,7 +628,7 @@ include ../_util-fns
- var decorated = lang == 'dart' ? 'annotated' : 'decorated'
- var decoratedCn = lang == 'dart' ? '注解' : '装饰器'
- var any_decorator = lang == 'dart' ? '' : 'TypeScript generates metadata for any class with a decorator, and any decorator will do.'
- var any_decoratorCn = lang == 'dart' ? '' : 'TypeScript会为任何带有装饰器的类生成元数据且会为任何装饰器都生成。'
- var any_decoratorCn = lang == 'dart' ? '' : 'TypeScript会为任何带有装饰器的类生成元数据且会为任何装饰器都生成。'
.callout.is-helpful
header Suggestion: add @Injectable() to every service class
header 建议:为每一个服务类添加@Injectable()
@ -637,10 +636,10 @@ include ../_util-fns
We recommend adding `@Injectable()` to every service class, even those that don't have dependencies
and, therefore, do not technically require it. Here's why:
我们建议为每个服务类都添加`@Injectable()`装饰器,即使它们因为没有任何依赖,而在技术上并不需要它。这是因为:
我们建议为每个服务类都添加`@Injectable()`装饰器,即使它们因为目前没有任何依赖,而在技术上并不需要它。这是因为:
ul(style="font-size:inherit")
li <b>Future proofing:</b> No need to remember <code>@Injectable()</code> when we add a dependency later.
li <b>面向未来:</b> 在我们将来添加依赖时,不必记着添加<code>@Injectable()</code>。
li <b>面向未来:</b> 在我们将来添加依赖时,不用怕忘了添加<code>@Injectable()</code>。
li <b>Consistency:</b> All services follow the same rules, and we don't have to wonder why #{a_decorator} is missing.
li <b>统一性:</b> 所有服务都遵循着同样的规则,我们就不必困惑为什么#{a_decoratorCn}被省略了。
@ -649,7 +648,7 @@ include ../_util-fns
Some developers prefer to add it only where needed and that's a reasonable policy too.
虽然我们建议给所有服务类都添加`@Injectable()`,但你也不必受此约束。
有些开发人员就喜欢在需要的时候才添加,那也同样是一个合理的准则。
有些开发人员就喜欢在需要的时候才添加,那也同样是一个合理的准则。
.l-sub-section
:marked
@ -664,13 +663,13 @@ include ../_util-fns
// #enddocregion di-injectable-2
.callout.is-critical
header Always include the parentheses
header 总要带着括号
header 总要带着括号
:marked
Always use `@Injectable()`, not just `@Injectable`.
Our application will fail mysteriously if we forget the parentheses.
总是使用`@Injectable()`的形式,不能只用`@Injectable`。
如果忘了括号,我们的应用就神不知鬼不觉的失败!
如果忘了括号,我们的应用就神不知鬼不觉的失败!
// #docregion logger-service-1
.l-main-section
@ -678,7 +677,8 @@ include ../_util-fns
## Creating and registering a logger service
## 创建和注册日志服务
We're injecting a logger into our `HeroService` in two steps:
我们要把日志服务注入到`HeroService`中需要两步:
要把日志服务注入到`HeroService`中需要两步:
1. Create the logger service.
1. 创建日志服务。
1. Register it with the application.
@ -697,8 +697,8 @@ include ../_util-fns
so we put it at the root level of the application in the `app/` folder, and
we register it in the `providers` array of the metadata for our application root component, `AppComponent`.
我们很可能在应用的任何地方都使用同一个日志服务实例。
所以,我们把它放到`app/`目录下,也就是应用的顶级,并把它注册到我们的根组件`AppComponent`上,放到元数据中的`providers`数组里。
我们很可能在应用的任何地方都使用同一个日志服务实例。
所以,我们把它放到`app/`目录下,也就是应用的顶级,并把它注册到我们的根组件`AppComponent`上,放到元数据中的`providers`数组里。
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger', 'app/app.component.ts (节选)')
// #docregion logger-service-3
:marked
@ -728,9 +728,8 @@ code-example(format, language="html").
这个“创建链”始于`Logger`的供应商。这个*供应商*就是我们下一节的主题。
But wait! What if the logger is optional?
<a id="optional"></a>
但,等一下,如果这个日志服务是可选的呢?
,等一下,如果这个日志服务是可选的呢?
<a id="optional"></a>
### Optional dependencies
### 可选的依赖
@ -770,12 +769,12 @@ code-example(format, language="html").
the injector will inject `null`. We have a method that logs.
What can we do to avoid a null reference exception?
准备一个空的日志服务。如果我们在代码中不注册一个,注入器就会注入`null`。
假设我们有一个带日志的方法,该如何消除空指针错误呢?
准备迎接一个空的日志服务。如果我们在代码中不注册一个,注入器就会注入`null`。
我们有一个需要记日志的方法,这时该如何消除空指针错误呢?
We could substitute a *do-nothing* logger stub so that calling methods continue to work:
我们可以用一个 *什么也不做* 的日志服务桩对象来代替,以便调用它的方法可以照常工作:
我们可以用一个*什么也不做*的日志服务桩对象来代替,以便调用它的方法可以照常工作:
// #enddocregion logger-service-6
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-logger')(format='.')
// #docregion logger-service-7
@ -805,7 +804,7 @@ code-example(format, language="html").
The injector relies on **providers** to create instances of the services
that the injector injects into components and other services.
供应商*供*所依赖值的一个具体的运行期版本。
供应商*供*所依赖值的一个具体的运行期版本。
注入器依靠**供应商们**来创建服务的实例,它会被注入器注入到组件或其它服务中。
We must register a service *provider* with the injector, or it won't know how to create the service.
@ -822,9 +821,9 @@ code-example(format, language="html").
- var implements = lang == 'dart' ? 'implements' : 'looks and behaves like a '
- var implementsCn = lang == 'dart' ? '实现' : '表现和行为像'
- var objectlike = lang == 'dart' ? '' : 'an object that behaves like '
- var objectlikeCn = lang == 'dart' ? '' : '一个对象,它的行为像'
- var objectlikeCn = lang == 'dart' ? '' : '一个对象,行为像'
- var loggerlike = lang == 'dart' ? '' : 'We could provide a logger-like object. '
- var loggerlikeCn = lang == 'dart' ? '' : '我们可以提供一个像logger的对象。'
- var loggerlikeCn = lang == 'dart' ? '' : '也就是可以提供一个像logger的对象。'
:marked
The `providers` array appears to hold a service class.
In reality it holds an instance of the [Provider](../api/core/Provider-class.html) class that can create that service.
@ -836,23 +835,23 @@ code-example(format, language="html").
The `Logger` class itself is an obvious and natural provider &mdash; it has the right shape and it's designed to be created.
But it's not the only way.
有很多方式可以 *提供* 一些#{implementsCn} `Logger`类的东西。
有很多方式可以*提供*一些#{implementsCn} `Logger`类的东西。
`Logger`类本身是一个显而易见而且自然而然的供应商 —— 它有正确的形态,并且它设计出来就是等着被创建的。
但它不是唯一的方式
但它不是唯一的选项
We can configure the injector with alternative providers that can deliver #{objectlike} a `Logger`.
We could provide a substitute class. #{loggerlike}
We could give it a provider that calls a logger factory function.
Any of these approaches might be a good choice under the right circumstances.
我们可以使用另外的各种供应商来配置这个注入器,只要它们能交付#{objectlikeCn}`Logger`。
我们可以使用另外的各种供应商来配置这个注入器,只要它们能交付#{objectlikeCn}`Logger`就可以了
我们可以提供一个替身类。#{loggerlikeCn}
我们可以给它一个供应商,让它调用一个可以创建日志服务的工厂函数。
所有这些方法,只要在正确的情境下,都可能是一个好的选择。
所有这些方法,只要用在正确的场合,都可能是一个好的选择。
What matters is that the injector has a provider to go to when it needs a `Logger`.
重点是:当注入器需要一个`Logger`时,它得先有一个供应商。
问题是:当注入器需要一个`Logger`时,它得先有一个供应商。
// #enddocregion providers-2
// #docregion providers-provide-1
:marked
@ -910,18 +909,18 @@ code-example(format, language="html").
第二个是一个供应商定义对象。
我们可以把它看做一个指导如何创建依赖值的*配方*。
有很多方式创建依赖值…… 也有很多方式可以写菜谱
有很多方式创建依赖值…… 也有很多方式可以写配方
// #docregion providers-alternative-1
:marked
<a id="class-provider"></a>
### Alternative class providers
### 另外的类供应商
### 另外的供应商
Occasionally we'll ask a different class to provide the service.
The following code tells the injector
to return a `BetterLogger` when something asks for the `Logger`.
某些时候,我们会请求另一个类来供此服务。
某些时候,我们会请求另一个类来供此服务。
下列代码告诉注入器:当有人请求一个`Logger`时,请返回一个`BetterLogger`。
// #enddocregion providers-alternative-1
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-4')
@ -959,13 +958,13 @@ code-example(format, language="html").
When the *old* component logs a message with `OldLogger`,
we want the singleton instance of `NewLogger` to handle it instead.
*老的* 组件想使用`OldLogger`记录消息时,我们希望改用`NewLogger`的单例对象来记录。
当*老的*组件想使用`OldLogger`记录消息时,我们希望改用`NewLogger`的单例对象来记录。
The dependency injector should inject that singleton instance
when a component asks for either the new or the old logger.
The `OldLogger` should be an alias for `NewLogger`.
当组件请求无论是新的还是老的日志服务时,依赖注入器注入的都应该是个单例对象。
当组件请求无论是新的还是老的日志服务时,依赖注入器注入的都应该是同一个单例对象。
也就是说,`OldLogger`应该是`NewLogger`的一个别名。
We certainly do not want two different `NewLogger` instances in our app.
@ -993,7 +992,7 @@ code-example(format, language="html").
:marked
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
有时,提供一个只读模式的对象会比请求注入器从类创建它更容易。
有时,提供一个预先做好的对象会比请求注入器从类中创建它更容易。
+makeExample('dependency-injection/ts/app/providers.component.ts','silent-logger')(format=".")
:marked
Then we register a provider with the `useValue` option,
@ -1028,7 +1027,7 @@ code-example(format, language="html").
Only authorized users should see secret heroes.
我们通过添加一个新的业务需求来说明这一点:
HeroService必须对普通用户里隐藏 *秘密* 英雄。
HeroService必须对普通用户隐藏掉*秘密*英雄。
只有获得授权的用户才能看到秘密英雄。
Like the `EvenBetterLogger`, the `HeroService` needs a fact about the user.
@ -1054,7 +1053,7 @@ code-example(format, language="html").
:marked
Instead the `HeroService` constructor takes a boolean flag to control display of secret heroes.
让`HeroService`的构造函数带上一个逻辑型的标志,来控制是否显示隐藏的英雄。
让`HeroService`的构造函数带上一个布尔型的标志,来控制是否显示隐藏的英雄。
// #enddocregion providers-factory-1
+makeExample('dependency-injection/ts/app/heroes/hero.service.ts','internals', 'app/heroes/hero.service.ts (节选)')(format='.')
// #docregion providers-factory-2
@ -1093,9 +1092,9 @@ code-example(format, language="html").
The `Logger` and `UserService` classes serve as tokens for their own class providers.
The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
`deps`属性是一个[供应商token](#token)的数组。
`deps`属性是一个[供应商Token](#token)的数组。
`Logger`和`UserService`类作为它们自身供应商的token。
注入器解析这些token并且把相应的服务注入到工厂函数参数中所对应的参数中去。
注入器解析这些Token并且把相应的服务注入到工厂函数参数中所对应的参数中去。
// #enddocregion providers-factory-4
// #docregion providers-factory-5
- var lang = current.path[1]
@ -1138,16 +1137,16 @@ code-example(format, language="html").
The injector maintains an internal *token-provider* map that it references when
asked for a dependency. The token is the key to the map.
当我们使用注入器注册一个供应商时实际上是把这个供应商和一个DI token关联起来了。
注入器维护一个内部的*token-供应商*映射表,这个映射表会在请求一个依赖时被引用到。
token就是这个映射表中的键值key
当我们使用注入器注册一个供应商时实际上是把这个供应商和一个DI Token关联起来了。
注入器维护一个内部的*Token-供应商*映射表,这个映射表会在请求一个依赖时被引用到。
Token就是这个映射表中的键值key
In all previous examples, the dependency value has been a class *instance*, and
the class *type* served as its own lookup key.
Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
在以前的所有范例中,依赖值都是一个类 *实例* ,并且类的 *类型* 作为它自己的查找键值。
这种情况下,我们实际上是直接从注入器中以`HeroService`类型作为token来获取一个`HeroService` 实例。
在以前的所有范例中,依赖值都是一个类*实例*,并且类的*类型*作为它自己的查找键值。
这种情况下,我们实际上是直接从注入器中以`HeroService`类型作为Token来获取一个`HeroService` 实例。
// #enddocregion tokens-1
+makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.')
// #docregion tokens-2
@ -1158,7 +1157,7 @@ code-example(format, language="html").
service associated with that `HeroService` class token:
当我们写一个请求注入基于类的依赖的构造函数时,我们是幸运的。
我们只要以`HeroService`类为类型定义一个构造函数参数Angular就会知道把跟`HeroService`类这个token关联的服务注入进来
我们只要以`HeroService`类为类型定义一个构造函数参数Angular就会知道把跟`HeroService`类这个Token关联的服务注入进来
// #enddocregion tokens-2
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-8-ctor')(format=".")
// #docregion tokens-3
@ -1202,8 +1201,8 @@ code-example(format, language="html").
我们想让这个`config`对象在注入时可用。
我们已经知道可以使用一个[值供应商](#value-provider)来注册一个对象。
但是这种情况下我们要把什么用作token呢
我们没办法找一个类来当做token因为没有`Config`类。
但是这种情况下我们要把什么用作Token呢
我们没办法找一个类来当做Token因为没有`Config`类。
// Typescript only
<a id="interface"></a>
@ -1241,8 +1240,7 @@ h3 OpaqueToken
p.
The solution is to define and use an !{opaquetoken}.
The definition looks like this:
解决方案是定义和使用用一个!{opaquetoken}不透明的Token。定义方式类似于这样
p 解决方案是定义和使用用一个!{opaquetoken}不透明的Token。定义方式类似于这样
// #enddocregion tokens-opaque-1
+makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.')
:marked
@ -1260,7 +1258,8 @@ p.
Now we can inject the #{configuration} object into any constructor that needs it, with
the help of an `@Inject` #{decorator} that tells Angular how to find the #{configuration} dependency value.
现在,我们可以把这个#{configurationCn}对象注入到任何需要它的构造函数中,在`@Inject`#{decoratorCn}的帮助下告诉Angular如何找到这个#{configuration}依赖的值。
现在,我们可以把这个#{configurationCn}对象注入到任何需要它的构造函数中,在`@Inject`#{decoratorCn}的帮助下,
告诉Angular如何找到这个#{configurationCn}依赖的值。
// #enddocregion tokens-opaque-2
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9b-ctor')(format=".")
@ -1293,7 +1292,7 @@ p.
adding a parameter to a constructor.
在本章中我们学会了Angular依赖注入的基础。
我们可以注册很多种类的供应商,而且我们知道了该如何通过添加构造函数的参数来请求一个被注入对象(比如服务)。
我们可以注册很多种类的供应商,知道了该如何通过添加构造函数的参数来请求一个被注入对象(比如服务)。
Angular dependency injection is more capable than we've described.
We can learn more about its advanced features, beginning with its support for
@ -1301,7 +1300,7 @@ p.
[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) chapter.
Angular依赖注入比我们描述的更能干。
我们还可以学到它的更多高级特性,从它对嵌套注入器的支持开始,就在[分层依赖注入](hierarchical-dependency-injection.html)一章。
我们还可以学到它的更多高级特性,从它对嵌套注入器的支持开始,参见[分层依赖注入](hierarchical-dependency-injection.html)一章。
// #enddocregion summary
// #docregion appendix-explicit-injector-1
@ -1327,7 +1326,7 @@ p.
The component then asks the injected injector for the services it wants.
在这个例子中Angular把组件自身的`Injector`注入到了组件的构造函数中。
然后组件注入进来的这个注入器请求它所需的服务。
然后组件注入进来的这个注入器请求它所需的服务。
Note that the services themselves are not injected into the component.
They are retrieved by calling `injector.get`.
@ -1340,14 +1339,14 @@ p.
to retrieve a service (`ROUS`) that isn't registered with this or any ancestor injector.
`get`方法如果解析不出所请求的服务,它就会抛出一个异常。
我们还可以带上第二个参数(如果服务没找到,就返回)调用`get`
在该例子中,我们获取一个服务(`ROUS`),它没有在这个注入器或它的任何祖先中注册过。
我们还可以带上第二个参数(如果服务没找到,就把它作为默认值返回)调用`get`
在该例子中,我们获取一个服务(`ROUS`),它没有在这个注入器或它的任何祖先中注册过。
.l-sub-section
:marked
The technique we just described is an example of the
[service locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern).
我们只通过一个范例描述这项技术:[服务定位器模式](https://en.wikipedia.org/wiki/Service_locator_pattern)。
我们只通过一个范例描述这项技术:[服务定位器模式](https://en.wikipedia.org/wiki/Service_locator_pattern)。
We **avoid** this technique unless we genuinely need it.
It encourages a careless grab-bag approach such as we see here.
@ -1356,7 +1355,7 @@ p.
It could acquire services from any ancestor component, not just its own.
We're forced to spelunk the implementation to discover what it does.
我们要 **避免使用** 此技术,除非我们确实需要它。
我们要**避免使用**此技术,除非我们确实需要它。
它会鼓励鲁莽的方法,就像我们在这里看到的。
它难以解释、理解和测试。
仅通过阅读构造函数,我们没法知道这个类需要什么或者它将做什么。
@ -1366,7 +1365,7 @@ p.
Framework developers may take this approach when they
must acquire services generically and dynamically.
框架开发人员可能需要此方法 —— 当他们不得不以通用和动态的方式获取服务时。
框架开发人员可能需要此方法 —— 当他们不得不以通用和动态的方式获取服务时。
// #enddocregion appendix-explicit-injector-2
// TypeScript only? Unnecessary for Dart
@ -1388,8 +1387,8 @@ p.
If we define the component before the service,
we'll get a runtime null reference error.
如果我们蔑视这个建议,并且 —— 比如说 —— 把`HeroService`和`HeroesComponent`组合在同一个文件里,**就得把组件定义在后面!**
如果我们把组件定义在服务的前面,我们就会在运行时获得一个空指针错。
如果我们蔑视这个建议,并且 —— 比如说 —— 把`HeroService`和`HeroesComponent`组合在同一个文件里,**就得把组件定义在后面!**
如果我们把组件定义在服务的前面,就会在运行时获得一个空指针错
.l-sub-section
:marked