用Dagger 2依赖注入 - DI介绍(翻译)
不久之前,在克拉科夫的 Tech Space 的 Google I/O 扩展中,我 展示 了一些关于使用Dagger 2来进行依赖注入。在准备期间我认识到有太多相关的东西需要去讲,无法用一打幻灯片就能覆盖到全部。但是它可以作为一个很好的进入点来开展更多这一系列主题-Android端的依赖注入。 在这一章中我会去通过之前所展示的来进行一个总结。可能并不是按部就班的 - 我认为现在是时候打破过去,使用一些原本我们不会使用或者不应该使用的方法来解决问题了。Jake Wharton 讲述 了相关历史(Guice,Dagger 1),Gregory Kick 也是(几乎有一半是关于Spring,Guice,Dagger 1)。我也会花几分钟的时间讲述以前的解决方式。但是此刻是时候开始了。 依赖注入依赖注入的全部就是构建对象并在我们需要时把它们传入。我不会深入到它的学说(查看维基百科对DI的定义)。想象一个简单的类:
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();
}
}
为什么这些代码会给我们制造一些问题呢?让我们想象一下,你希望去改变 现在再来看下我们使用了依赖注入的 它的依赖是在类的外面创建和提供的: 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();
}
}
现在在相似的情况下 - 我们改变它其中一个依赖的实现方式 - 我们不需要修改 所以使用依赖注入的优势是什么呢? 构造/使用 的分离当我们构造类的实例 - 通常这些对象会在其它的地方被使用到,多亏这个方法让我们的代码更加模块化 - 所有的依赖都可以被很简单地替换掉(只要他们实现了相同的接口),并且不会与我们应用的逻辑产生冲突。想要改变 单元测试(Unit testing)真正的单元测试假设一个类是可以完全被隔离进行测试的 - 不需要了解它的相关依赖。在实践中,基于我们的 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 - 多亏 独立/并行开发多亏模块化的代码( 依赖注入框架依赖注入除了这些优点之外还有一些缺点。其中一个缺点是会产生很大的模版代码。想象一个简单的 唯一有问题的部分代码就是 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之上并且会用在我的展示中。一个小提示 - More detailed description - soon. 参考
作者Miroslaw Stanek Head of Mobile Development @ Azimo
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |