依赖注入与Service Locator
- 为什么需要依赖注入?

- ServiceUser是组件,在编写者之外的环境内被使用,且使用者不能改变其源代码.
- ServiceProvider是服务,其类似于ServiceUser,都要被其他应用使用,不同是ServiceProvider会被用于非本地环境,需要对应不同环境.
- 普通的ServiceUser来负责直接创建所需Service实现的实例方法.拥有以下的局限
- 在不同的环境下,ServiceProvider是千差万别的(数据库,临时文件,内存).
- 所以,不能将ServiceUser作为组件发布(适应不了各种差异环境).
- 为了将ServiceUser所在单元作为组件发布,必须满足以下的条件.
- 将ServiceUser与具体的ServiceProvider_Imp解耦(解除编译时依赖).即不能出现new ServiceProvider_Imp()语句.
- 在运行时,根据环境来为ServiceUser来动态地提供ServiceProvider的实现实例.
- 总之,将ServiceUser和ServiceProvider之间的依赖,由编译时,推迟到运行时动态决定.
- 即在运行时"注入"这种依赖,称为依赖注入(DI).
- ServiceUser可以看做框架.其需要对外发布,被其他模块使用.
- ServiceProvider可看做插件.其在不同的环境下有差别.
- 依赖注入
- 在编译时,使用IServiceProvider接口.在运行时,再将具体的实现类型绑定到ServiceUser上.
- 从而实现了服务的使用者(框架)和服务的提供者(插件)的松耦合.
- 加入一个Assembler(容器)来完成对框架单独发布所需的要求.
- 构造器DI
- 构造器指的是ServiceUser的构造器,也就是在构造ServiceUser的实例时,才把具体的ServiceProvider_实现传递给它.
class ServiceUser
{
IServiceProvider sp;
ServiceUser(IServiceProver sp)
{
this.sp = sp;
}
} private MutablePicoContainer configureContainer() {
MutablePicoContainer pico = new DefaultPicoContainer();
//下面就是把ServiceProvider和ServiceUser都放入容器的过程,以后就由容器来提供ServiceUser的已完成依赖注入实例,
//其中用到的实例参数和类型参数一般是从配置档中读取的,这里是个简单的写法。
Parameter[] finderParams = {new ConstantParameter("movies1.txt")};
pico.registerComponentImplementation(IServiceProvider.class, ServiceProvider.class, finderParams);
pico.registerComponentImplementation(ServiceUser.class);
//至此,容器里面装入了两个类型,其中没给出构造参数的那一个(ServiceUser)将依靠其在构造器中定义的传入参数类型,在容器中
//进行查找,找到一个类型匹配项即可进行构造初始化。
return pico;
}CtorDI
- 依赖被延迟到容器的构造过程中.
- 容器本身(具体来说是其构造过程)对ServiceUser和ServiceProvider都有依赖.
- 所以,ServiceUser,ServiceProvider,容器三者,没有任何的依赖关系.
- 所有的依赖关系,所有的变化,都被封装到了容器的构造中.
- 实例项目中,可以使用配置文件来将变化排斥到编译期外.
- 容器含有一个类似GetInstance(Type t)的方法,在该方法中,容器调用ServiceUser的构造器,而使用的参数是在容器添加ServiceProvider时指定的(也就能够在不同的环境下,使用不同的ServiceProvider的实现).
- 属性DI
- 其与构造器DI的差别
- 在获取对象实例时(GetInstance方法),前者通过反射得到待创建类型(ServiceUser)的构造器信息,然后根据构造器的参数类型(ServiceProvider)在容器中进行查找,然后构造出合适的实例.
- 而属性DI是通过反射得到待创建类型的所有属性,然后根据属性的类型在容器中查找对应类型的实例.
- 这种方式利于使用XML配置的方式来实现,所以诸如Spring的框架使用的都是这种方式.
- 其与构造器DI的差别
- 接口DI
- 首先,在接口中定义需要注入的信息.
- 然后,在ServiceUser中实现该接口.
- 最后,由容器调用接口定义的注入方法来完成注入.
- Service Locator

- 基本思想:有一个对象(服务定位器)知道如何获取一个应用程序所需的所有服务(对象).
- 与DI的区别
- ServiceUser必须显示调用Service Locator的方法来获取对应的服务对象(ServiceProvider)实例.
- 而在DI中,这个过程是由容器隐式完成的.
- 由于ServiceUser和Servive Locator之间的依赖性,降低了模块之间的独立性,所以IOC框架大多使用的是DI.
- 而Locator的优势是实现简单,所以当开发工作不复杂时,可以使用Service Locator.
- 由于单个ServiceLocator会导致很多的方法接口.可以提取专用的Get接口.然后对应的ServiceUser只关心和使用其对应的接口.
- DI和Locator的对比
- 首先,两者的目的都是解耦ServiceUser和ServiceProvider的实现.
- 在构筑大型应用时,会遇到一个共同的问题:如何组合系统的不同部分.
- 例如,web控制器如何与后台的数据库层面交互.
- 尤其是当这些不同部分是由不同的team完成,互相之间只有很少的了解.
- 核心区别在于,ServiceProvider的实现,以什么方式提供给ServiceUser.
- 在Locator中,ServiceUser直接发送方法调用来请求具体的ServiceProvider实现的实例.
- 在DI中,ServiceUser不再需要任何的调用,由容器来隐式地完成将ServiceProvider的实现实例注入到ServiceUser中的过程.
- 控制反转(IOC)是框架的基本特征.
- 增加了框架代码的理解难度,同时增加了调试难度.
- 在选择两者的取舍时,主要考虑的是(ServiceUser)对Locator的依赖会不会造成问题.
- 首先,取决于ServiceUser的性质,如果有多个ServiceUser要使用同一服务,那么可以使用Locator.
- 而如果要将ServiceUser作为组件发布供别人使用,而别人的Locator是不可预测的,很可能带来不兼容问题,所以要使用DI.
- 另一方面, 在DI中,容器和ServiceProvider的实现没有依赖关系.除了配置文件信息,服务实现无法获取更多的关于容器的信息.
- 使用DI模式,可以更清晰地理清组件间的依赖关系.
- 只需要观察依赖注入机制(例如构造函数).就可以看到整个的依赖关系.
- 而使用Locator模式,必须在源代码中搜索所有对Locator的调用才可以.
- 测试
- 部分人会认为DI简化了测试,因为可以非常简单地在真实和伪组件(ServiceProvider的实现)之间切换.
- 但是,良好的Locator实现,应该能做到很容易地替换掉Locator.
- 也就是说,良好的架构应该支持简单地使用一个伪组件来替换掉一个真实组件,来方便地进行测试.
- 所以,如果ServiceProvider的实现如果要脱离自己的控制,在另一个环境中使用,那么就不能对Locator做任何的假定.
- 构造DI对比属性DI
- 基准:应该在那里填充字段(属性)的值?构造函数还是设置方法?
- 构造函数的一个好处是隐藏不可变的字段.
- 在构造器内将其设置,然后不提供Setter即可.
- 构造DI的问题
- 参数个数过多
- 参数多是无法描述信息的简单类型.
- 对象含有多个构造函数,且有继承关系.
- 代码配置对比配置文件
- 在简单的应用中,直接使用代码来完成配置.
- 当配置文件变得越发复杂时,使用一种编程语言来编写配置文件.
- 分离配置和使用
- 关键:服务的配置和使用应该被分离开.
- 实际上,这反映了一个基本的设计原则:分离接口与实现.
- 在面向对象的程序中,我们在一个地方根据条件来决定具体实例化哪一个子类,之后使用多态(接口类型)来操作该类型.
- DI的目的
- 用来降低主程序与对象间的关联.
- 同时也能减低对象间的关联.
- 简化对象的建立动作,进而让对象更加容易使用.
依赖注入与Service Locator的更多相关文章
- dotnet core在Task中使用依赖注入的Service/EFContext
C#:在Task中使用依赖注入的Service/EFContext dotnet core时代,依赖注入基本已经成为标配了,这就不多说了. 前几天在做某个功能的时候遇到在Task中使用EF DbCon ...
- .Net Core 学习依赖注入自定义Service
1. 定义一个服务,包含一个方法 public class TextService { public string Print(string m) { return m; } } 2. 写一个扩展方法 ...
- [ASP.NET Core 3框架揭秘] 依赖注入[3]:依赖注入模式
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制权从应用转移到框架之中以实现对流程的复用,并按照"好莱坞法则"实现应用程序的代码与框架之间的交互.我们可以采用若干设计模式 ...
- [ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式
原文:[ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式 IoC主要体现了这样一种设计思想:通过将一组通用流程的控制权从应用转移到框架之中以实现对流程的复用,并按照“好莱坞法则”实现应用 ...
- Yii2.0 依赖注入(DI)和依赖注入容器的原理
依赖注入和依赖注入容器 为了降低代码耦合程度,提高项目的可维护性,Yii采用多许多当下最流行又相对成熟的设计模式,包括了依赖注入(Denpdency Injection, DI)和服务定位器(Serv ...
- Unity文档阅读 第二章 依赖注入
Introduction 介绍Chapter 1 outlines how you can address some of the most common requirements in enterp ...
- yii依赖注入和依赖注入容器
依赖注入和依赖注入容器¶ 为了降低代码耦合程度,提高项目的可维护性,Yii采用多许多当下最流行又相对成熟的设计模式,包括了依赖注入(Denpdency Injection, DI)和服务定位器(Ser ...
- yii依赖注入
为了降低代码耦合程度,提高项目的可维护性,Yii采用多许多当下最流行又相对成熟的设计模式,包括了依赖注入(Denpdency Injection, DI)和服务定位器(Service Locator) ...
- ASP.NET设计模式(一)、适配器模式、依赖注入依赖倒置、空对象模式
鸟随凤鸾,人伴贤良,得以共之,我之幸也.说的是鸟随着鸾凤可以飞的更高远,人和比自己境界高的相处,自己也会得到熏染进步. 一.概述 分享出来简单的心得,望探讨 依赖倒置 依赖注入 Adapter模式 N ...
随机推荐
- uni-app强大的前端框架,h5,原生app(两大系统),微信小程序
最近发现一款强大的前端框架,它叫uni-app 这是一款通用的框架可以打包app,h5,微信小程序, 说说要弄这个工具需要会那些技能吧, 要熟悉vue,微信小程序.这样这个框架用的就是很快上手了 模块 ...
- PAT_A1087#All Roads Lead to Rome
Source: PAT A1087 All Roads Lead to Rome (30 分) Description: Indeed there are many different tourist ...
- eas之日期控件
日期选择框能进行日期和时间的编辑,默认情况下只能进行日期选择“××××年××月××日”,可通过调用用函数setTimeEnabled(boolean)来设置是否也有时间编辑.对日期进行编辑时,可手工直 ...
- 6.2 C# 2:利用 yield 语句简化迭代器
class Program { static void Main(string[] args) { object[] values = new object[] { "a", &q ...
- Centos7从3.10升级内核到4.9后无法启动解决办法:mpt[23]sas驱动问题
Centos7升级内核后无法启动解决办法:mpt[23]sas驱动问题 前言 这个问题存在有一段时间了,之前做的centos7的ISO,在进行内核的升级以后就存在这个问题: 系统盘在板载sata口上是 ...
- 在LinuxMint19上安装搜狗拼音输入法
写在前面 由于Linux mint是基于Ubuntu的深度改造,所以按照网上针对Ubuntu的安装方法基本都是有用的.LinuxMint自身就携带了IBUS和fcitx两个框架.然而并非每次都能正常使 ...
- 【ZOJ 4067】Books
[链接] 我是链接,点我呀:) [题意] [题解] 统计a中0的个数cnt0 然后m减去cnt0 因为这cnt0个0是一定会取到的. 如果m==0了 那么直接找到数组中的最小值mi 输出mi-1就好 ...
- adchos 文本混淆工具
#-*- coding:utf-8 -*- import jieba import random import codecs import sys import string import chard ...
- Tarjan算法求解桥和边双连通分量(附POJ 3352 Road Construction解题报告)
http://blog.csdn.net/geniusluzh/article/details/6619575 在说Tarjan算法解决桥和边双连通分量问题之前我们先来回顾一下Tarjan算法是如何 ...
- hdu 2586 lca在线算法(朴素算法)
#include<stdio.h> #include<string.h>//用c/c++会爆栈,用g++ac #define inf 0x3fffffff #define N ...
