以下内容为原创,欢迎转载,转载请注明

来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5092083.html

使用Dagger 2依赖注入 - DI介绍

原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-introdution-to-di/

不久之前,在克拉科夫的 Tech Space 的 Google I/O 扩展中,我 展示 了一些关于使用Dagger 2来进行依赖注入。在准备期间我认识到有太多相关的东西需要去讲,无法用一打幻灯片就能覆盖到全部。但是它可以作为一个很好的进入点来开展更多这一系列主题-Android端的依赖注入。

在这一章中我会去通过之前所展示的来进行一个总结。可能并不是按部就班的 - 我认为现在是时候打破过去,使用一些原本我们不会使用或者不应该使用的方法来解决问题了。Jake Wharton 讲述 了相关历史(Guice, Dagger 1),Gregory Kick 也是(几乎有一半是关于Spring, Guice, Dagger 1)。我也会花几分钟的时间讲述以前的解决方式。但是此刻是时候开始了。

依赖注入

依赖注入的全部就是构建对象并在我们需要时把它们传入。我不会深入到它的学说(查看维基百科对DI的定义)。想象一个简单的类:UserManager,它依赖UserStoreApiService。如果没有使用依赖注入,这个类会看起来像这样:

UserStoreApiService 两者都是在UserManager类中构造和提供的:

class UserManager {

    private ApiService apiService;
private UserStore userStore; //No-args constructor. Dependencies are created inside.
public UserManager() {
this.apiService = new ApiSerivce();
this.userStore = new UserStore();
} void registerUser() {/* */} } class RegisterActivity extends Activity { private UserManager userManager; @Override
protected void onCreate(Bundle b) {
super.onCreate(b);
this.userManager = new UserManager();
} public void onRegisterClick(View v) {
userManager.registerUser();
}
}

为什么这些代码会给我们制造一些问题呢?让我们想象一下,你希望去改变UserStore的实现,用SharedPreferences来作为它的存储机制。它需要至少一个Context对象来创建一个实例,所以我们需要把它通过构造器传入到UserStore。它意味着UserManager类中也需要被修改来使用新的UserStore构造器。现在想象下有很多类使用了UserStore - 它们全部都需要被修改。

现在再来看下我们使用了依赖注入的UserManager类:

它的依赖是在类的外面创建和提供的:

class UserManager {

    private ApiService apiService;
private UserStore userStore; //Dependencies are passed as arguments
public UserManager(ApiService apiService, UserStore userStore) {
this.apiService = apiService;
this.userStore = userStore;
} void registerUser() {/* */} } class RegisterActivity extends Activity { private UserManager userManager; @Override
protected void onCreate(Bundle b) {
super.onCreate(b);
ApiService api = ApiService.getInstance();
UserStore store = UserStore.getInstance(); this.userManager = new UserManager(api, store);
} public void onRegisterClick(View v) {
userManager.registerUser();
} }

现在在相似的情况下 - 我们改变它其中一个依赖的实现方式 - 我们不需要修改UserManager源代码。所有它的依赖都是从外面提供的,所以我们唯一一个需要修改的地方就是我们构造的UserStore对象。

所以使用依赖注入的优势是什么呢?

构造/使用 的分离

当我们构造类的实例 - 通常这些对象会在其它的地方被使用到,多亏这个方法让我们的代码更加模块化 - 所有的依赖都可以被很简单地替换掉(只要他们实现了相同的接口),并且不会与我们应用的逻辑产生冲突。想要改变DatabaseUserStoreSharedPrefsUserStore ?好的,只需要关心公开的API(与DatabaseUserStore相同的)或者实现相同的接口。

单元测试(Unit testing)

真正的单元测试假设一个类是可以完全被隔离进行测试的 - 不需要了解它的相关依赖。在实践中,基于我们的UserManager类,这里有一个我们应该编写的单元测试的例子:

public class UserManagerTests {

    UserManager userManager;

    @Mock
ApiService apiServiceMock;
@Mock
UserStore userStoreMock; @Before
public void setUp() {
MockitoAnnotations.initMocks(this);
userManager = new UserManager(apiServiceMock, userStoreMock);
} @After
public void tearDown() {
} @Test
public void testSomething() {
//Test our userManager here - all its dependencies are satisfied
}
}

它可能只能使用DI - 多亏UserManager是完全独立于UserStoreApiService实现的。我们可以提供这些类的mock(简单地说 - mocks是一些拥有相同公开API的类,它在方法中不做任何事情并且/或者返回我们期望的值),然后在一个与所依赖的真实实现分离出来的环境下进行对UserManager的测试。

独立/并行开发

多亏模块化的代码(UserStore可以从UserManager中独立出来进行实现),它也可以非常方便在程序员间进行代码的分离。只需要UserStore相关的接口被每个人知道(尤其是在UserManager中使用到的UserStore中的公开方法)即可。剩下的(实现,逻辑)可以通过单元测试来测试。

依赖注入框架

依赖注入除了这些优点之外还有一些缺点。其中一个缺点是会产生很大的模版代码。想象一个简单的LoginActivity类,它在MVP(model-view-presenter)模式中被实现。这个类看起来就像这样:

唯一有问题的部分代码就是LoginActivityPresenter的初始化,如下:

public class LoginActivity extends AppCompatActivity {

    LoginActivityPresenter presenter;

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); OkHttpClient okHttpClient = new OkHttpClient();
RestAdapter.Builder builder = new RestAdapter.Builder();
builder.setClient(new OkClient(okHttpClient));
RestAdapter restAdapter = builder.build();
ApiService apiService = restAdapter.create(ApiService.class);
UserManager userManager = UserManager.getInstance(apiService); UserDataStore userDataStore = UserDataStore.getInstance(
getSharedPreferences("prefs", MODE_PRIVATE)
); //Presenter is initialized here
presenter = new LoginActivityPresenter(this, userManager, userDataStore);
}
}

它看起来不太友好,不是吗?

这就是DI框架需要解决的问题。相同功能的代码看起来就像这样:

public class LoginActivity extends AppCompatActivity {

    @Inject
LoginActivityPresenter presenter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //Satisfy all dependencies requested by @Inject annotation
getDependenciesGraph().inject(this);
}
}

简单多了,对吧?当然DI框架没有地方可以获取到对象 - 他们仍然需要在我们代码的某个地方进行初始化和配置。但是对象构建从使用中分离出来了(实质上这是DI模式的准则)。DI框架关心怎么样去把它们联系在一起(怎么在对象被需要时分配给它们)。

未完待续

我上面所有描述的东西都是使用Dagger 2的简单的背景 - 用于Android和Java开发的依赖注入框架。在下一章我将尝试讲解所有Dagger 2的API。如果你等不急可以尝试我的Github client example,它建立在Dagger 2之上并且会用在我的展示中。一个小提示 - @Module@Component就是构建/提供对象的地方。@Inject是我们对象使用到的地方。

More detailed description - soon.

参考

作者

Miroslaw Stanek

Head of Mobile Development @ Azimo

> __[Android]使用Dagger 2依赖注入 - DI介绍(翻译):__

> __[Android]使用Dagger 2依赖注入 - API(翻译):__

> __[Android]使用Dagger 2依赖注入 - 自定义Scope(翻译):__

> __[Android]使用Dagger 2依赖注入 - 图表创建的性能(翻译):__

> __[Android]Dagger2Metrics - 测量DI图表初始化的性能(翻译):__

> __[Android]使用Dagger 2进行依赖注入 - Producers(翻译):__

> __[Android]在Dagger 2中使用RxJava来进行异步注入(翻译):__

> __[Android]使用Dagger 2来构建UserScope(翻译):__

> __[Android]在Dagger 2中Activities和Subcomponents的多绑定(翻译):__

[Android]使用Dagger 2依赖注入 - DI介绍(翻译)的更多相关文章

  1. [Android]使用Dagger 2依赖注入 - API(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5092525.html 使用Dagger 2依赖注入 - API ...

  2. [Android]使用Dagger 2依赖注入 - 自定义Scope(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5095426.html 使用Dagger 2依赖注入 - 自定义 ...

  3. [Android]使用Dagger 2依赖注入 - 图表创建的性能(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5098943.html 使用Dagger 2依赖注入 - 图表创 ...

  4. [Android]使用Dagger 2进行依赖注入 - Producers(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6234811.html 使用Dagger 2进行依赖注入 - P ...

  5. [Android]使用Dagger 2来构建UserScope(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6237731.html 使用Dagger 2来构建UserSco ...

  6. Spring框架学习笔记(1)——控制反转IOC与依赖注入DI

    Spring框架的主要作用,就是提供了一个容器,使用该容器就可以创建并管理对象.比如说Dao类等,又或者是具有多依赖关系的类(Student类中包含有Teacher类的成员变量) Spring有两个核 ...

  7. 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。

    轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战   ...

  8. 浅析“依赖注入(DI)/控制反转(IOC)”的实现思路

    开始学习Spring的时候,对依赖注入(DI)——也叫控制反转(IOC)—— 的理解不是很深刻.随着学习的深入,也逐渐有了自己的认识,在此记录,也希望能帮助其他入门同学更深入地理解Spring.本文不 ...

  9. 控制反转(Ioc)和依赖注入(DI)

    控制反转IOC, 全称 “Inversion of Control”.依赖注入DI, 全称 “Dependency Injection”. 面向的问题:软件开发中,为了降低模块间.类间的耦合度,提倡基 ...

随机推荐

  1. JavaScript中undefined与null的区别

    通常情况下, 当我们试图访问某个不存在的或者没有赋值的变量时,就会得到一个undefined值.Javascript会自动将声明是没有进行初始化的变量设为undifined. 如果一个变量根本不存在会 ...

  2. 使用Masstransit开发基于消息传递的分布式应用

    Masstransit作为.Net平台下的一款优秀的开源产品却没有得到应有的关注,这段时间有机会阅读了Masstransit的源码,我觉得我有必要普及一下这个框架的使用. 值得一提的是Masstran ...

  3. SSH框架整合(代码加文字解释)

    一.创建数据库并设置编码. A) create database oa default character set utf8. 二.MyEclipse工程 A) 在Myeclipse里创建web工程, ...

  4. WCF学习之旅—TcpTrace工具(二十五)

    前面的几篇文章,我们学习了怎么开发WCF应用程序与服务,也学习了如何进行WCF的配置.对于Web Service与WCF服务应用,服务端与客户端的通信是通过收发SOAP Message进行,我们如何有 ...

  5. [WPF]控件应用多个样式

    最近在做WPF项目,公司没有专门的UI工程师,什么都要自己做.接触WPF已经有好几年了,自定义样式什么的也可以做一些.WPF在使用样式的时候一般都是 Style="{StaticResour ...

  6. WebGIS中基于控制点库进行SHP数据坐标转换的一种查询优化策略

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.前言 目前项目中基于控制点库进行SHP数据的坐标转换,流程大致为:遍 ...

  7. angularjs之filter过滤器

    现在公司用ionic,就是基于angularjs封装了一些api用于webapp,最近用的angularjs的filter确实省了很多代码,现在总结一下! ng比较鸡肋的过滤器,这里就一笔带过吧!鸡汤 ...

  8. 在CentOS 7/6.5/6.4 中安装Java JDK 8(转载)

    转载在CentOS 7/6.5/6.4 中安装Java JDK 8 首先,在你的服务器上运行一下更新. yum update 然后,在您的系统上搜索,任何版本的已安装的JDK组件. rpm -qa | ...

  9. 嵌入式服务器jetty,让你更快开发web

    概述 jetty是什么? jetty是轻量级的web服务器和servlet引擎. 它的最大特点是:可以很方便的作为嵌入式服务器. 它是eclipse的一个开源项目.不用怀疑,就是你常用的那个eclip ...

  10. 在ASP.NET Core Web API上使用Swagger提供API文档

    我在开发自己的博客系统(http://daxnet.me)时,给自己的RESTful服务增加了基于Swagger的API文档功能.当设置IISExpress的默认启动路由到Swagger的API文档页 ...