如下內容爲原創,歡迎轉載,轉載請註明
來自每天博客:http://www.cnblogs.com/tiantianbyconan/p/5092083.html
html
原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-introdution-to-di/java
不久以前,在克拉科夫的 Tech Space 的 Google I/O 擴展中,我 展現 了一些關於使用Dagger 2來進行依賴注入。在準備期間我認識到有太多相關的東西須要去講,沒法用一打幻燈片就能覆蓋到所有。可是它能夠做爲一個很好的進入點來開展更多這一系列主題-Android端的依賴注入。git
在這一章中我會去經過以前所展現的來進行一個總結。可能並非循序漸進的 - 我認爲如今是時候打破過去,使用一些本來咱們不會使用或者不該該使用的方法來解決問題了。Jake Wharton 講述 了相關歷史(Guice, Dagger 1),Gregory Kick 也是(幾乎有一半是關於Spring, Guice, Dagger 1)。我也會花幾分鐘的時間講述之前的解決方式。可是此刻是時候開始了。程序員
依賴注入的所有就是構建對象並在咱們須要時把它們傳入。我不會深刻到它的學說(查看維基百科對DI的定義)。想象一個簡單的類:UserManager
,它依賴UserStore
和ApiService
。若是沒有使用依賴注入,這個類會看起來像這樣:github
UserStore
和 ApiService
二者都是在UserManager
類中構造和提供的:api
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
類:異步
它的依賴是在類的外面建立和提供的:ide
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
對象。模塊化
因此使用依賴注入的優點是什麼呢?
當咱們構造類的實例 - 一般這些對象會在其它的地方被使用到,多虧這個方法讓咱們的代碼更加模塊化 - 全部的依賴均可以被很簡單地替換掉(只要他們實現了相同的接口),而且不會與咱們應用的邏輯產生衝突。想要改變DatabaseUserStore
爲SharedPrefsUserStore
?好的,只須要關心公開的API(與DatabaseUserStore
相同的)或者實現相同的接口。
真正的單元測試假設一個類是能夠徹底被隔離進行測試的 - 不須要了解它的相關依賴。在實踐中,基於咱們的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
是徹底獨立於UserStore
和ApiService
實現的。咱們能夠提供這些類的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.
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