[Android]使用Dagger 2依赖注入 - DI介绍(翻译)
以下内容为原创,欢迎转载,转载请注明
来自天天博客: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
,它依赖UserStore
和ApiService
。如果没有使用依赖注入,这个类会看起来像这样:
UserStore
和 ApiService
两者都是在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;@Overrideprotected 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 argumentspublic UserManager(ApiService apiService, UserStore userStore) {this.apiService = apiService;this.userStore = userStore;}void registerUser() {/* */}}class RegisterActivity extends Activity {private UserManager userManager;@Overrideprotected 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
对象。
所以使用依赖注入的优势是什么呢?
构造/使用 的分离
当我们构造类的实例 - 通常这些对象会在其它的地方被使用到,多亏这个方法让我们的代码更加模块化 - 所有的依赖都可以被很简单地替换掉(只要他们实现了相同的接口),并且不会与我们应用的逻辑产生冲突。想要改变DatabaseUserStore
为SharedPrefsUserStore
?好的,只需要关心公开的API(与DatabaseUserStore
相同的)或者实现相同的接口。
单元测试(Unit testing)
真正的单元测试假设一个类是可以完全被隔离进行测试的 - 不需要了解它的相关依赖。在实践中,基于我们的UserManager
类,这里有一个我们应该编写的单元测试的例子:
public class UserManagerTests {UserManager userManager;@MockApiService apiServiceMock;@MockUserStore userStoreMock;@Beforepublic void setUp() {MockitoAnnotations.initMocks(this);userManager = new UserManager(apiServiceMock, userStoreMock);}@Afterpublic void tearDown() {}@Testpublic void testSomething() {//Test our userManager here - all its dependencies are satisfied}
}
它可能只能使用DI - 多亏UserManager
是完全独立于UserStore
和ApiService
实现的。我们可以提供这些类的mock(简单地说 - mocks是一些拥有相同公开API的类,它在方法中不做任何事情并且/或者返回我们期望的值),然后在一个与所依赖的真实实现分离出来的环境下进行对UserManager
的测试。
独立/并行开发
多亏模块化的代码(UserStore
可以从UserManager
中独立出来进行实现),它也可以非常方便在程序员间进行代码的分离。只需要UserStore
相关的接口被每个人知道(尤其是在UserManager
中使用到的UserStore
中的公开方法)即可。剩下的(实现,逻辑)可以通过单元测试来测试。
依赖注入框架
依赖注入除了这些优点之外还有一些缺点。其中一个缺点是会产生很大的模版代码。想象一个简单的LoginActivity
类,它在MVP(model-view-presenter)模式中被实现。这个类看起来就像这样:
唯一有问题的部分代码就是LoginActivityPresenter
的初始化,如下:
public class LoginActivity extends AppCompatActivity {LoginActivityPresenter presenter;@Overrideprotected 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 herepresenter = new LoginActivityPresenter(this, userManager, userDataStore);}
}
它看起来不太友好,不是吗?
这就是DI框架需要解决的问题。相同功能的代码看起来就像这样:
public class LoginActivity extends AppCompatActivity {@InjectLoginActivityPresenter presenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//Satisfy all dependencies requested by @Inject annotationgetDependenciesGraph().inject(this);}
}
简单多了,对吧?当然DI框架没有地方可以获取到对象 - 他们仍然需要在我们代码的某个地方进行初始化和配置。但是对象构建从使用中分离出来了(实质上这是DI模式的准则)。DI框架关心怎么样去把它们联系在一起(怎么在对象被需要时分配给它们)。
未完待续
我上面所有描述的东西都是使用Dagger 2的简单的背景 - 用于Android和Java开发的依赖注入框架。在下一章我将尝试讲解所有Dagger 2的API。如果你等不急可以尝试我的Github client example,它建立在Dagger 2之上并且会用在我的展示中。一个小提示 - @Module
和@Component
就是构建/提供对象的地方。@Inject
是我们对象使用到的地方。
More detailed description - soon.
参考
- DAGGER 2 - A New Type of dependency injection by Gregory Kick
- The Future of Dependency Injection with Dagger 2 by Jake Wharton
- Dagger 1 to 2 migration process
作者
Miroslaw Stanek
Head of Mobile Development @ Azimo
[Android]使用Dagger 2依赖注入 - DI介绍(翻译):
http://www.cnblogs.com/tiantianbyconan/p/5092083.html
[Android]使用Dagger 2依赖注入 - API(翻译):
http://www.cnblogs.com/tiantianbyconan/p/5092525.html
[Android]使用Dagger 2依赖注入 - 自定义Scope(翻译):
http://www.cnblogs.com/tiantianbyconan/p/5095426.html
[Android]使用Dagger 2依赖注入 - 图表创建的性能(翻译):
http://www.cnblogs.com/tiantianbyconan/p/5098943.html
[Android]Dagger2Metrics - 测量DI图表初始化的性能(翻译):
http://www.cnblogs.com/tiantianbyconan/p/5193437.html
[Android]使用Dagger 2进行依赖注入 - Producers(翻译):
http://www.cnblogs.com/tiantianbyconan/p/6234811.html
[Android]在Dagger 2中使用RxJava来进行异步注入(翻译):
http://www.cnblogs.com/tiantianbyconan/p/6236646.html
[Android]使用Dagger 2来构建UserScope(翻译):
http://www.cnblogs.com/tiantianbyconan/p/6237731.html
[Android]在Dagger 2中Activities和Subcomponents的多绑定(翻译):
http://www.cnblogs.com/tiantianbyconan/p/6266442.html
关注 - 7
粉丝 - 215
» 下一篇:[Android]使用Dagger 2依赖注入 - API(翻译)
[Android]使用Dagger 2依赖注入 - DI介绍(翻译)相关推荐
- [Android]使用Dagger 2依赖注入 - 自定义Scope(翻译)
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5095426.html 使用Dagger 2依赖注入 - 自定义 ...
- [Android]使用Dagger 2进行依赖注入 - Producers(翻译)
使用Dagger 2进行依赖注入 - Producers 原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-produc ...
- Android 依赖注入 DI - Dagger2
1.依赖注入 (Dependency Injection) 1.1 面向接口编程 public interface Drivable {void drive(); }public class Bike ...
- 控制反转(Ioc)和依赖注入(DI)
控制反转IOC, 全称 "Inversion of Control".依赖注入DI, 全称 "Dependency Injection". 面向的问题:软件开发 ...
- PHP依赖注入(DI)和控制反转(IoC)详解
这篇文章主要介绍了PHP依赖注入(DI)和控制反转(IoC)的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程 ...
- 控制反转(IoC)与依赖注入(DI)详解
文章目录 什么是控制反转(IoC) 控制反转(IoC)有什么作用 控制反转(IoC)是怎么分类的 依赖注入 接口注入 Setter方法注入 构造器注入 依赖查找 上下文依赖查找(Contextuali ...
- 控制反转IOC、依赖注入DI的详细说明与举例
文章目录 引入 IOC介绍 IOC的实现 通过构造函数注入依赖 通过 setter 设值方法注入依赖 依赖注入容器 IOC优缺点 优点 缺点 阅读时忽略语言差异,参考了很多其他博主内容,参考博文在最后 ...
- 依赖注入(DI)和Ninject,Ninject
我们所需要的是,在一个类内部,不通过创建对象的实例而能够获得某个实现了公开接口的对象的引用.这种"需要",就称为DI(依赖注入,Dependency Injection),和所谓的 ...
- 依赖倒置(DIP),控制反转(IoC)与依赖注入(DI)
DIP,IoC与DI概念解析 依赖倒置 DIP(Dependency Inversion Principle) DIP的两大原则: 1.高层模块不应该依赖于低层模块,二者都应该依赖于抽象. 2.抽象不 ...
最新文章
- LINUX下c语言调用math.h库函数的注意事项
- 远程访问×××——Easy ×××
- 【Flask】Flask-RESTful 风格编程
- ALV GRID中实现RadioButton单选按钮
- 博士申请 | 阿联酋阿布扎比人工智能大学许志强老师招收全奖博士/硕士
- iPhone 9上架了?5月1日发货?
- 跟着辛星用PHP的反射机制来实现插件
- matlab mxarray赋值,C++中数组与MATLAB mxArray相互赋值
- 程序员真的是太太太太太太太太难了!
- static 结构体_C++基础-static
- 整理全网可视化大屏有关资料,学习大屏设计,大屏原型参考,大屏设计参考
- abap语言去除重复项怎么写
- 15.4 MPLS 控制层面 PE和CE设备间的路由更新
- 格雷码在异步FIFO中的应用
- iOS开启个人热点的纵向适配
- java基础知识点整理大全
- Python 强化学习实用指南:1~5
- TortoiseSVN 设置中文语言包
- 进制转换方法总结(详细图解)
- 魅蓝E3发布,斥资千万购买虹软算法!
热门文章
- linux+手机+翻页,在Android手机上实现阅读器的翻页效果
- mysql 服务账号_MySql 账号管理
- win10玩cf不能全屏_游戏莫名卡顿三招搞定!Win10游戏优化教程
- cropper左右移动_移动端cropper.js上传图片、裁剪
- 下列有关计算机图形的叙述中错误的是,计算机图形学(9-16)-中国大学mooc-题库零氪...
- 函数头文件php_PHP 7.4 的 FFI 将支持更好地在 PHP 中使用 C 函数/数据结构
- c++实现rsa算法_RSA简介
- 集合詳細學習 包括Vector ArrayList List等等的比較與實例
- 模块A:大数据平台搭建(容器环境)
- Spring Boot基础学习笔记21:自定义用户认证