博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Android]使用Dagger 2依赖注入 - DI介绍(翻译)
阅读量:6266 次
发布时间:2019-06-22

本文共 5438 字,大约阅读时间需要 18 分钟。

以下内容为原创,欢迎转载,转载请注明
来自天天博客:

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

原文:

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

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

依赖注入

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

user_manager_no_di.png

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类:

user_manager_di.png

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

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)模式中被实现。这个类看起来就像这样:

login_activity_diagram.png

唯一有问题的部分代码就是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。如果你等不急可以尝试我的,它建立在Dagger 2之上并且会用在我的展示中。一个小提示 - @Module@Component就是构建/提供对象的地方。@Inject是我们对象使用到的地方。

More detailed description - soon.

参考

  • by Gregory Kick
  • by Jake Wharton

作者

Head of Mobile Development @

[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的多绑定(翻译):

你可能感兴趣的文章
lock关键字理解
查看>>
20172303 2018-2019-1 《程序设计与数据结构》第3周学习总结
查看>>
[Javascript]史上最短的IE浏览器判断代码
查看>>
关于《大道至简》第五章的收获
查看>>
网卡驱动
查看>>
kinect sdk开发入门WPFdemo笔记
查看>>
Server.Transfer详细解释
查看>>
java单链表的增、删、查操作
查看>>
The working copy at 'xxx' is too old 错误解决
查看>>
jadclipse
查看>>
// 1.什么是JOSN?
查看>>
SQL语句详细汇总
查看>>
如何保护.net中的dll文件(防破解、反编译)
查看>>
Python 装饰器
查看>>
Docker 网络模式
查看>>
[POI2013]Usuwanka
查看>>
problem-solving-with-algorithms-and-data-structure-usingpython(使用python解决算法和数据结构) -- 算法分析...
查看>>
nodejs + CompoundJS 资源
查看>>
转:C#并口热敏小票打印机打印位图
查看>>
scau 17967 大师姐唱K的固有结界
查看>>