ServiceScope class
SharePoint 框架使用的服务定位符模式。
注解
ServiceScope 为组件提供了一种形式化的方式,用于注册和使用 (“服务”) 的依赖项,并允许在不同范围内注册不同的实现。 这通过以可扩展方式将组件与其依赖项分离,从而提高了模块化。
例如,假设各种组件需要访问 IPageManager 实例。 我们只需将 PageManager 设置为单一实例 (即全局变量) ,但这将不起作用,例如,如果需要创建需要第二个 PageManager 实例的弹出对话框。 更好的解决方案是将 PageManager 添加为需要它的每个组件的构造函数参数,但是,我们立即面临以下问题:调用这些构造函数的任何代码也需要 PageManager 参数。 在具有许多此类依赖项的应用程序中,将多个子系统联系在一起的业务逻辑最终会为每个可能的依赖项选取构造函数参数,这很尴尬。 自然的解决方案是将所有依赖项移动到名为“ApplicationContext”的类中,然后将其作为构造函数参数传递。 这使 PageManager 能够传递给需要它的类,而不会使不需要的中介类混乱。 但是,它仍然存在一个设计问题,即“ApplicationContext”在许多不相关的事情上具有硬编码依赖项。 一种更灵活的方法是使其成为一个字典,以便为知道正确查找键 ((即 ServiceKey) )的使用者/提供者查找项。 这是常用的“服务定位符”设计模式,从经典 SharePoint 中的 SPContext API 中很熟悉。
ServiceScope 在两个重要方面进一步推进了这一想法:首先,它提供了一个范围界定机制,例如,如果我们有两个不同的页面,它们都可以提供唯一的 PageManager 实例,同时仍共享其他常见依赖项。 其次,它允许 ServiceKey 提供依赖项的默认实现。 这一点对于模块化客户端环境中的 API 稳定性非常重要:例如,假设应用程序版本 2.0 引入了一个新的 IDiagnosticTracing 接口,2.0 版组件将期望使用该接口。 如果 2.0 版组件由较旧的 1.0 应用程序加载,则会失败。 我们可以通过要求每个使用者检查任何缺少的依赖项并处理这种情况来解决此问题,但这需要大量的检查。 更好的解决方案是确保默认实现始终存在(可能只是一种微不足道的行为),以便组件可以假定使用 () 将始终返回实现协定的某个对象。
用法:通过调用 ServiceScope.startNewRoot () 或 ServiceScope.startNewChild () 创建 ServiceScope 实例。 它们最初处于“未完成”状态,在此期间,可以调用提供 () 来注册服务密钥,但不允许使用 () 。 调用 ServiceScope.finish () 后,允许使用 () ,并提供 () 现已禁止。 这些语义可确保 ServiceScope.consume () 始终返回相同键的相同结果,并且不依赖于初始化顺序。 它还允许我们支持循环依赖项,而无需担心无限循环。 (循环依赖项是最好避免的,但是当使用由各种第三方提供的组件时,这很难保证。) 为了避免错误,最好始终调用 serviceScope.whenFinished () 回调内的消耗 () 。
构造函数
(constructor)(parent) | 构造 类的新实例 |
方法
consume(service |
使用服务范围内的服务。 |
create |
这是一个简写函数,等效于构造 simpleServiceClass 的新实例,然后通过调用 ServiceScope.provide () 进行注册。 |
create |
这是一个简写函数,可构造指定 serviceKey 的默认实现,然后通过调用 ServiceScope.provide() 注册它。 |
finish() | 完成服务范围的初始化序列。 |
get |
返回当前 ServiceScope 的父级,如果这是根范围,则返回 undefined。 |
provide(service |
将新服务添加到服务范围。 |
start |
构建一个新的 ServiceScope,它是当前作用域的子级。 |
start |
创建新的根级 ServiceScope。 仅根级别范围才具有自动创建 ServiceKeys 默认实现的功能。 |
when |
将操作推迟到 ServiceScope.finish () 完成后。 |
构造函数详细信息
(constructor)(parent)
构造 类的新实例ServiceScope
protected constructor(parent: ServiceScope | undefined);
参数
- parent
-
ServiceScope | undefined
方法详细信息
consume(serviceKey)
使用服务范围内的服务。
consume<T>(serviceKey: ServiceKey<T>): T;
参数
- serviceKey
-
ServiceKey<T>
调用 provide() 注册该服务时所使用的密钥
返回
T
服务实例
注解
组件应该调用此函数以“使用”依赖,即查找 serviceKey 并返回已注册的服务实例。 如果找不到实例,则会自动创建默认实例并将其注册到根 ServiceScope。
createAndProvide(serviceKey, simpleServiceClass)
这是一个简写函数,等效于构造 simpleServiceClass 的新实例,然后通过调用 ServiceScope.provide () 进行注册。
createAndProvide<T>(serviceKey: ServiceKey<T>, simpleServiceClass: {
new (serviceScope: ServiceScope): T;
}): T;
参数
- serviceKey
-
ServiceKey<T>
可稍后用于使用服务的密钥
- simpleServiceClass
-
{ new (serviceScope: ServiceScope): T; }
要构造的 TypeScript 类
返回
T
新构造的 simpleServiceClass 实例
createDefaultAndProvide(serviceKey)
这是一个简写函数,可构造指定 serviceKey 的默认实现,然后通过调用 ServiceScope.provide() 注册它。
createDefaultAndProvide<T>(serviceKey: ServiceKey<T>): T;
参数
- serviceKey
-
ServiceKey<T>
可稍后用于使用服务的密钥
返回
T
使用 ServiceKey.defaultCreator 构造的服务实例
finish()
完成服务范围的初始化序列。
finish(): void;
返回
void
注解
首次启动 ServiceScope 时,它处于“未完成”状态,允许提供 () ,但不允许使用 () 。 调用完成 () 后,使用 () 是允许的,但提供 () 是不允许的。
这种形式主义可以防止许多可能导致 bug 的复杂情况。 例如,假设 Scope2 是 Scope1 的子级,Scope1 提供接口 A1 的实例 A1。如果在使用 A2 调用 Scope2.provide () 之前通过继承) 从 Scope2 (使用 A1,则后续对 Scope2.consume () 的调用可能会返回与上一次调用不同的结果。 这种不确定可能导致难以诊断的不可预测的结果。
getParent()
返回当前 ServiceScope 的父级,如果这是根范围,则返回 undefined。
getParent(): ServiceScope | undefined;
返回
ServiceScope | undefined
父服务范围
provide(serviceKey, service)
将新服务添加到服务范围。
provide<T>(serviceKey: ServiceKey<T>, service: T): T;
参数
- serviceKey
-
ServiceKey<T>
稍后用于使用服务的键
- service
-
T
正在被注册的服务实例
返回
T
作为“service”参数进行传递的同一对象
注解
ServiceScope.provide () 用于为当前范围注册给定 serviceKey 的实现,并为根范围注册给定 rootServiceKey 的实现。 仅当 ServiceScope 处于“未完成”状态时才能使用它,即在 finish() 被调用之前。
startNewChild()
构建一个新的 ServiceScope,它是当前作用域的子级。
startNewChild(): ServiceScope;
返回
新创建的根 ServiceScope
注解
服务范围形成树结构,因此在使用服务时,如果键不是由子范围显式提供的,则会查阅父层次结构。
startNewRoot()
创建新的根级 ServiceScope。 仅根级别范围才具有自动创建 ServiceKeys 默认实现的功能。
static startNewRoot(): ServiceScope;
返回
新创建的根 ServiceScope
whenFinished(callback)
将操作推迟到 ServiceScope.finish () 完成后。
whenFinished(callback: () => void): void;
参数
- callback
-
() => void
需要调用 ServiceScope.consume() 的代码块
返回
void
注解
在 finish() 被调用前调用 ServiceScope.consume() 是错误的。 保护你的组件避免出现此错误的最可靠的方法是在 whenFinished() 回调内执行 consume() 调用。 如果已完成服务范围,则立即执行回调;否则,它将在范围完成后执行。
注意:这不是异步回调。 ServiceScope 初始化通常成本低廉且生存期较短。 但是,控制流通常通过许多构造函数和基类进行线程,可以使用 whenFinished () 来简化这些构造函数和基类。