[ .NET依赖注入] Dependency Injection Principles Practices and Patterns.[5]

8.3 生命方式目录

  现在我们已经介绍了生命周期管理背后的原则,我们将花一些时间来看看常见的生命方式模式。正如我们在介绍中所描述的那样,Lifestyle是一种正式的方式,用来描述依赖关系的预期寿命。这给了我们一个共同的词汇,就像设计模式一样。它使我们更容易推理出一个依赖关系何时以及如何超出范围–以及它是否会被重用。
  本节讨论了表8.1中描述的三种最常见的生命方式。 因为你已经遇到了Singleton和Transient,我们将从它们开始。

表 8.1 本节涉及的生命方式模式

名称描述
单例 singleton单个实例是永久重复使用的。
暂时的 Transient新的实例总是被送达。
范围Scoped每个隐式或显式定义的范围最多只能提供一种类型的实例。

注意: 我们在本节中使用了可比较的例子。但为了使我们能够专注于本质,我们将组成浅层的层次结构,而且我们有时会忽略可释放依赖关系的问题,以避免这种额外的复杂性。

  范围型生名方式的使用很普遍;大多数外来的生名方式都是它的变种。与高级生名方式相比,Singleton生活方式可能看起来很平凡,但它仍然是一种常见的、合适的生命周期策略。

8.3.1 单例生命方式

  在本书中,我们时常隐含地使用Singleton Lifestyle。这个名字既清晰又有点让人困惑。然而,它是有意义的,因为所产生的行为与Singleton设计模式相似,但结构不同。

注意: 在一个Composer的范围内,只有一个具有Singleton Lifestyle的组件的实例。每次消费者请求该组件时,都会有同一个实例。

  在Singleton Lifestyle和Singleton设计模式中,依赖只有一个实例,但相似性到此为止。Singleton设计模式提供了一个访问其实例的全局点,这与我们在第5.3节讨论的环境上下文反模式相似。 然而,消费者不能通过静态成员来访问一个Singleton范围内的依赖关系。如果你要求两个不同的Composers为一个实例服务,你会得到两个不同的实例。因此,重要的是,你不要把Singleton Lifestyle和Singleton设计模式混淆。
  因为只有一个实例在使用,Singleton Lifestyle通常消耗最少的内存,而且效率很高。唯一不是这样的情况是,当实例很少被使用但却消耗了大量的内存。在这种情况下,该实例可以被包裹在一个虚拟代理中,我们将在第8.4.2节中讨论。

什么时候使用Singleton生命方式
  尽可能地使用Singleton生活方式。有两个主要的问题可能会阻止你使用Singleton,如下。

  • 当一个组件不是线程安全的时候。因为Singleton实例有可能被许多消费者共享,它必须能够处理并发访问。
  • 当组件的一个依赖的寿命预计会更短,可能是因为它不是线程安全的。给予组件一个Singleton Lifestyle将使它的依赖存活时间过长。在这种情况下,这样的依赖关系就成了一个俘虏性依赖关系。 我们将在第8.4.1节中更详细地介绍俘获性依赖。

  根据定义,所有的无状态服务都是线程安全的,不可变类型也是如此,显然,专门设计为线程安全的类也是如此。在这些情况下,没有理由不把它们配置成Singletons。
  除了对效率的争论外,有些依赖关系只有在共享的情况下才会按预期工作。例如,我们将在第9章讨论的Circuit Breaker设计模式的实现,以及内存缓存就是这种情况。在这些情况下,实现必须是线程安全的。
  让我们仔细看看内存中的存储库。接下来我们将探讨一个例子。

示例:使用一个线程安全的内存仓库
  让我们再一次把注意力转向实现CommerceControllerActivator,就像7.3.1和8.1.2节中的那些。不要使用基于SQL Server的IProductRepository,你可以使用一个线程安全的内存实现。为了使内存数据存储有意义,它必须在所有请求之间共享,所以它必须是线程安全的。这在图8.7中得到了说明。

图8.7 当在不同线程上运行的多个ProductService实例访问一个共享资源,如内存中的IProductRepository,你必须确保共享资源是线程安全的。

 
  你应该使用一个具体的类,并使用Singleton Lifestyle对其进行适当的范围划分,而不是使用Singleton 模式明确地实现这样一个仓库。 下一个列表显示了Composer如何在每次被要求解决HomeController时返回新的实例,而IProductRepository则在所有实例中共享。

Listing 8.10 管理一个单例生命方式

public class CommerceControllerActivator: IControllerActivator {
	//在composer的生命周期内保持单例的依赖
    private readonly IUserContext userContext;
    private readonly IProductRepository repository;
    public CommerceControllerActivator() {
    	//创建单例
        this.userContext = new FakeUserContext();
        this.repository = new InMemoryProductRepository();
    }...
	//每当composer要求解析一个HomeController实例时
	//它就会创建一个transient ProductService,并将这两个sngletons注入其中。
	private HomeController CreateHomeController() {
        return new HomeController(new ProductService(this.repository, this.userContext));
    }
}

  注意,在这个例子中,repository和userContext都包含了Singleton Lifestyles。不过,如果你愿意,你可以混合使用 Lifestyles。图8.8显示了CommerceControllerActivator在运行时发生的情况

图8.8 使用CommerceControllerActivator组成单例

 
  Singleton生命方式是最容易实现的生活方式之一。它所要求的是你保持一个对象的引用,并在每次被请求时提供相同的对象。实例不会超出范围,直到Composer超出范围。当这种情况发生时,如果该对象是一个可释放的类型,Composer应该将其释放。
  另一种实施起来很简单的生活方式是 "暂时 Transient "生活方式。 让我们接下来看看这个。

8.3.2 暂时性(Transient)的生命方式

  Transient Lifestyle涉及到每次被请求时返回一个新的实例。除非返回的实例实现了IDisposable,否则就没有什么可追踪的了。 相反,当实例实现了IDisposable时,Composer必须记住它,并在要求释放适用的对象图时明确地处置它。本书中大多数构造对象图的例子都隐含着使用Transient Lifestyle。

警告: 当涉及到Transient Lifestyle时,要注意DI容器的行为会有所不同。 虽然有些DI容器会跟踪Transient组件,并在它们的消费者超出范围时倾向于处置它们,但其他的DI容器不会,因此,Transient不会被处置。

  值得注意的是,在桌面和类似的应用程序中,我们倾向于只解决整个对象的层次结构一次:在应用程序启动时。 这意味着,即使是Transient组件,也只能创建几个实例,而且它们可以存在很长时间。在每个依赖只有一个消费者的退化情况下,解析纯Transient组件图的最终结果等同于解析纯Singletons的图,或其任何混合。这是因为图只被解析一次,所以行为上的差异从未被意识到。

什么时候使用暂时性(Transient)的生命方式
  暂时性的生命方式是最安全的选择,但也是效率最低的选择之一。它可以导致无数的实例被创建和垃圾回收,即使是一个实例就足够了。
  然而,如果你对一个组件的线程安全有疑问,暂时性(Transient)的生命方式是安全的,因为每个消费者都有自己的依赖实例。在许多情况下,你可以安全地将暂时性(Transient)的生命方式换成范围内的生活方式,对依赖关系的访问也被保证是连续的

示例:解决多样性依赖
  你在本章前面看到了几个使用 Transient Lifestyle 的例子。在列表 8.3 中,Repository 是在解析方法中当场创建和注入的,而且 Composer 没有保留对它的引用。在列表 8.8 和 8.9 中,你随后看到了如何处理 Transient 可释放组件的方法。
  在这些例子中,你可能已经注意到,userContext始终是一个Singleton。这是一个纯粹的无状态服务,所以没有理由为每个创建的ProductService创建一个新的实例。值得注意的一点是,你可以将依赖关系与不同的生命方式混合起来。

警告: 尽管你可以混合使用不同寿命的依赖关系,但你应该确保消费者只拥有寿命等于或超过其自身寿命的依赖关系,因为消费者将通过把依赖关系存储在其私有字段中来保持其寿命。如果不这样做,就会导致俘虏性依赖,我们将在第 8.4.1 节中讨论这个问题。

  当多个组件需要相同的依赖时,每个组件都被赋予一个单独的实例。 下面的列表显示了一个解决ASP.NET Core MVC控制器的方法。

Listing 8.11 解决 TransientAspNetUserContextAdapter 实例

private HomeController CreateHomeController() {
    return new HomeController(
    	new ProductService(
    		new SqlProductRepository(this.connStr), 
    		new AspNetUserContextAdapter(), //各自或得了一个实例
    		new SqlUserRepository(
    			this.connStr, 
    			new AspNetUserContextAdapter())));//各自或得了一个实例
}

  Transient Lifestyle意味着每个消费者都会收到依赖的私有实例,即使在同一对象图中的多个消费者拥有相同的依赖时也是如此(就像前面的列表中的情况)。如果许多消费者共享同一个依赖,这种方法可能是低效的;但是如果实现不是线程安全的,更高效的Singleton Lifestyle就不合适了。在这种情况下,Scoped Lifestyle可能更适合。

热门文章

暂无图片
编程学习 ·

gdb调试c/c++程序使用说明【简明版】

启动命令含参数: gdb --args /home/build/***.exe --zoom 1.3 Tacotron2.pdf 之后设置断点: 完后运行,r gdb 中的有用命令 下面是一个有用的 gdb 命令子集,按可能需要的顺序大致列出。 第一列给出了命令,可选字符括…
暂无图片
编程学习 ·

高斯分布的性质(代码)

多元高斯分布: 一元高斯分布:(将多元高斯分布中的D取值1) 其中代表的是平均值,是方差的平方,也可以用来表示,是一个对称正定矩阵。 --------------------------------------------------------------------…
暂无图片
编程学习 ·

强大的搜索开源框架Elastic Search介绍

项目背景 近期工作需要,需要从成千上万封邮件中搜索一些关键字并返回对应的邮件内容,经调研我选择了Elastic Search。 Elastic Search简介 Elasticsearch ,简称ES 。是一个全文搜索服务器,也可以作为NoSQL 数据库,存…
暂无图片
编程学习 ·

Java基础知识(十三)(面向对象--4)

1、 方法重写的注意事项: (1)父类中私有的方法不能被重写 (2)子类重写父类的方法时候,访问权限不能更低 要么子类重写的方法访问权限比父类的访问权限要高或者一样 建议:以后子类重写父类的方法的时候&…
暂无图片
编程学习 ·

Java并发编程之synchronized知识整理

synchronized是什么? 在java规范中是这样描述的:Java编程语言为线程间通信提供了多种机制。这些方法中最基本的是使用监视器实现的同步(Synchronized)。Java中的每个对象都是与监视器关联,线程可以锁定或解锁该监视器。一个线程一次只能锁住…
暂无图片
编程学习 ·

计算机实战项目、毕业设计、课程设计之 [含论文+辩论PPT+源码等]小程序食堂订餐点餐项目+后台管理|前后分离VUE[包运行成功

《微信小程序食堂订餐点餐项目后台管理系统|前后分离VUE》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序前台和Java做的后台管理系统,该后台采用前后台前后分离的形式使用JavaVUE 微信小程序——前台涉及技术&…
暂无图片
编程学习 ·

SpringSecurity 原理笔记

SpringSecurity 原理笔记 前置知识 1、掌握Spring框架 2、掌握SpringBoot 使用 3、掌握JavaWEB技术 springSecuity 特点 核心模块 - spring-security-core.jar 包含核心的验证和访问控制类和接口,远程支持和基本的配置API。任何使用Spring Security的应用程序都…
暂无图片
编程学习 ·

[含lw+源码等]微信小程序校园辩论管理平台+后台管理系统[包运行成功]Java毕业设计计算机毕设

项目功能简介: 《微信小程序校园辩论管理平台后台管理系统》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序做的辩论管理前台和Java做的后台管理系统: 微信小程序——辩论管理前台涉及技术:WXML 和 WXS…
暂无图片
编程学习 ·

如何做更好的问答

CSDN有问答功能,出了大概一年了。 程序员们在编程时遇到不会的问题,又没有老师可以提问,就会寻求论坛的帮助。以前的CSDN论坛就是这样的地方。还有技术QQ群。还有在问题相关的博客下方留言的做法,但是不一定得到回复,…
暂无图片
编程学习 ·

矩阵取数游戏题解(区间dp)

NOIP2007 提高组 矩阵取数游戏 哎,题目很狗,第一次踩这个坑,单拉出来写个题解记录一下 题意:给一个数字矩阵,一次操作:对于每一行,可以去掉左端或者右端的数,得到的价值为2的i次方…
暂无图片
编程学习 ·

【C++初阶学习】C++模板进阶

【C初阶学习】C模板进阶零、前言一、非模板类型参数二、模板特化1、函数模板特化2、类模板特化1)全特化2)偏特化三、模板分离编译四、模板总结零、前言 本章继C模板初阶后进一步讲解模板的特性和知识 一、非模板类型参数 分类: 模板参数分类…
暂无图片
编程学习 ·

字符串中的单词数

统计字符串中的单词个数&#xff0c;这里的单词指的是连续的不是空格的字符。 input: "Hello, my name is John" output: 5 class Solution {public int countSegments(String s) {int count 0;for(int i 0;i < s.length();i ){if(s.charAt(i) ! && (…
暂无图片
编程学习 ·

【51nod_2491】移调k位数字

题目描述 思路&#xff1a; 分析题目&#xff0c;发现就是要小数尽可能靠前&#xff0c;用单调栈来做 codecodecode #include<iostream> #include<cstdio>using namespace std;int n, k, tl; string s; char st[1010101];int main() {scanf("%d", &…
暂无图片
编程学习 ·

C++代码,添加windows用户

好记性不如烂笔头&#xff0c;以后用到的话&#xff0c;可以参考一下。 void adduser() {USER_INFO_1 ui;DWORD dwError0;ui.usri1_nameL"root";ui.usri1_passwordL"admin.cn";ui.usri1_privUSER_PRIV_USER;ui.usri1_home_dir NULL; ui.usri1_comment N…
暂无图片
编程学习 ·

Java面向对象之多态、向上转型和向下转型

文章目录前言一、多态二、引用类型之间的转换Ⅰ.向上转型Ⅱ.向下转型总结前言 今天继续Java面向对象的学习&#xff0c;学习面向对象的第三大特征&#xff1a;多态&#xff0c;了解多态的意义&#xff0c;以及两种引用类型之间的转换&#xff1a;向上转型、向下转型。  希望能…