Android中的依賴注入類型和選擇
優(yōu)勢:
- Reusability of code
- Ease of refactoring
- Ease of testing
什么是依賴注入
舉個不是依賴注入的例子
class Car {
private Engine engine = new Engine();
public void start() {
engine.start();
}
}
class MyApp {
public static void main(String[] args) {
Car car = new Car();
car.start();
}
}
在這個例子中违柏,Car 類構(gòu)造了它自己的Engine紧卒,這將會有這些問題:
- Car和Engine 緊密耦合,一個Car的實(shí)例只能使用一種Engine姻氨,沒有子類或者可替代的實(shí)現(xiàn)可以被簡單的使用。如果Car構(gòu)建了自己的Engine,當(dāng)你要使用Gas或者Electric 類型的Engines的時(shí)候你將創(chuàng)建兩種類型的Car,而不是重用相同的Car享扔,
- 會使測試更加的困難
什么樣的代碼看起來像是依賴注入呢?取代Car初始化的時(shí)候構(gòu)建自己的Engine植袍,以接收Engine對象作為一個參數(shù)的方式來代替惧眠。
class Car {
private final Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
class MyApp {
public static void main(String[] args) {
Engine engine = new Engine();
Car car = new Car(engine);
car.start();
}
}
在main方法中 先創(chuàng)建Engine實(shí)例然后作為參數(shù)來構(gòu)造Car實(shí)例。這種基于依賴注入方法的好處是:
- Car類可以重復(fù)利用奋单,你可以傳遞不同實(shí)現(xiàn)的Engine給Car锉试,比如說你可以定義一個你想讓Car使用的Engine子類ElectricEngine,如果你使用依賴注入览濒,你所需要做的僅僅是將上述代碼中的Engine實(shí)例更新為ElectricEngine實(shí)例呆盖,Car的代碼不需要做任何改動仍然可以運(yùn)作。
- Car類更容易進(jìn)行測試
Android中兩種主要的依賴注入方式:
構(gòu)造器注入贷笛。這是上面code所描述的方式应又,你通過一個類的構(gòu)造器給它傳遞依賴
字段注入(或者Setter注入)。一些Android Framework 類比如說Activity類和Fragment類已經(jīng)被系統(tǒng)實(shí)例化了乏苦,所以構(gòu)造器注入是不可行的株扛,通過字段注入,依賴項(xiàng)的實(shí)例化可以在這些類被創(chuàng)建后汇荐,代碼如下洞就。
class Car {
private Engine engine;
public void setEngine(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
class MyApp {
public static void main(String[] args) {
Car car = new Car();
car.setEngine(new Engine());
car.start();
}
}
自動依賴注入
在上面的示例中,你自己創(chuàng)建掀淘,提供和管理了不同類的依賴關(guān)系旬蟋,而不需要依賴庫。這稱之為手動依賴注入革娄,
Car的例子中只有一個依賴項(xiàng)倾贰,但是更多的依賴項(xiàng)和類用手動依賴注入將會變得冗長乏味。手動依賴注入也顯示出了幾個問題:
- 對于大型App拦惋,獲取所有依賴項(xiàng)并正確連接它們可能需要大量的樣板代碼匆浙。 在多層體系結(jié)構(gòu)中,為了為頂層創(chuàng)建對象厕妖,您必須提供其下層的所有依賴關(guān)系首尼。 舉一個具體的例子,要制造一輛真正的汽車,您可能需要引擎饰恕,變速箱挠羔,底盤和其他零件井仰。 發(fā)動機(jī)又需要?dú)飧缀突鸹ㄈ?/li>
- 當(dāng)您無法在傳遞依賴項(xiàng)之前構(gòu)造依賴項(xiàng)時(shí)(例如埋嵌,在使用延遲初始化或?qū)ο笞饔糜虼_定為應(yīng)用程序流時(shí)),您需要編寫并維護(hù)一個自定義容器(或依賴關(guān)系圖)俱恶,以管理您的生命周期 內(nèi)存中的依賴項(xiàng)雹嗦。
有一些庫通過自動化創(chuàng)建和提供依賴項(xiàng)的過程來解決這個問題。它們可分為兩類:
- 基于反射的解決方案合是,在運(yùn)行時(shí)連接依賴項(xiàng)了罪。
- 靜態(tài)解決方案,在編譯時(shí)生成代碼連接依賴項(xiàng)聪全。
Dagger 是一個流行的依賴注入庫泊藕,可用于Java,Kotlin,Android,它由谷歌維護(hù)难礼。Dagger通過創(chuàng)建和管理依賴關(guān)系圖娃圆,讓你在App中使用依賴注入提供了便利。它提供完全靜態(tài)和編譯時(shí)依賴關(guān)系蛾茉,解決了基于反射解決方案(例如Guice)的許多開發(fā)和性能問題讼呢。
依賴項(xiàng)注入的替代方法
依賴項(xiàng)注入的替代方法是使用服務(wù)定位器。 服務(wù)定位器設(shè)計(jì)模式還改善了類與具體依賴關(guān)系的解耦谦炬。 您創(chuàng)建一個稱為服務(wù)定位器的類悦屏,該類創(chuàng)建并存儲依賴項(xiàng),然后根據(jù)需要提供這些依賴項(xiàng)键思。
class ServiceLocator {
private static ServiceLocator instance = null;
private ServiceLocator() {}
public static ServiceLocator getInstance() {
if (instance == null) {
synchronized(ServiceLocator.class) {
instance = new ServiceLocator();
}
}
return instance;
}
public Engine getEngine() {
return new Engine();
}
}
class Car {
private Engine engine = ServiceLocator.getInstance().getEngine();
public void start() {
engine.start();
}
}
class MyApp {
public static void main(String[] args) {
Car car = new Car();
car.start();
}
}
服務(wù)定位器模式與依賴項(xiàng)注入的不同之處在于元素的使用方式础爬。 使用服務(wù)定位器模式,類可以控制并要求注入對象吼鳞; 通過依賴注入看蚜,該應(yīng)用程序可以控制并主動注入所需的對象。
與依賴注入對比:
- 服務(wù)定位器所需的依賴項(xiàng)集合使代碼更難測試赖条,因?yàn)樗袦y試都必須與相同的全局服務(wù)定位器進(jìn)行交互失乾。
- 依賴項(xiàng)是在類實(shí)現(xiàn)中編碼的,而不是在API表面中纬乍。因此碱茁,很難從外部了解一個類需要什么。因此仿贬,對Car或服務(wù)定位器中可用的依賴項(xiàng)的更改可能導(dǎo)致引用失敗纽竣,從而導(dǎo)致運(yùn)行時(shí)或測試失敗。
- 如果您希望將范圍擴(kuò)大到整個應(yīng)用程序的生存期以外的任何地方,那么管理對象的生存期就比較困難蜓氨。
為您的應(yīng)用選擇正確的技術(shù)
如上所述聋袋,這里有集中不同的技術(shù)去管理你的應(yīng)用的依賴:
- 手動依賴注入 僅適用于相對小的app,因?yàn)樗臄U(kuò)展性差穴吹,當(dāng)項(xiàng)目變得龐大幽勒,傳遞對象會要求很多模板代碼。
- 服務(wù)定位器 從相對較少的樣板代碼開始港令,但是擴(kuò)展性也很差啥容。此外,測試變得更加困難顷霹,因?yàn)樗鼈円蕾囉趩卫龑ο蟆?/li>
- Dagger 為擴(kuò)展而創(chuàng)建咪惠。它非常適合構(gòu)建復(fù)雜的應(yīng)用程序
項(xiàng)目大小 | 小 | 中 | 大 |
---|---|---|---|
使用的工具 | 手動注入 服務(wù)定位器 Dagger | Dagger | Dagger |
如果你的小型app 似乎有可能增長,當(dāng)沒有太多代碼需要修改時(shí)淋淀。你應(yīng)該趁早考慮遷移到Dagger遥昧。
總結(jié)
依賴注入可以給你的app提供以下優(yōu)勢:
類的可重用性和依賴解耦:換出依賴項(xiàng)的實(shí)現(xiàn)會更容易。 由于控制反轉(zhuǎn)朵纷,因此代碼重用得到了改善炭臭,并且類不再控制其依賴關(guān)系的創(chuàng)建方式,而是可以與任何配置一起使用柴罐。
易于重構(gòu):依賴關(guān)系成為API表面的可驗(yàn)證部分徽缚,因此可以在對象創(chuàng)建時(shí)或在編譯時(shí)對其進(jìn)行檢查,而不必將其隱藏為實(shí)現(xiàn)細(xì)節(jié)革屠。
易于測試:一個類部管理他自己的依賴凿试,因此當(dāng)你測試它的時(shí)候,你可以傳給它不同實(shí)現(xiàn)的實(shí)現(xiàn)去測試你不同的的用例
為了更好的立即依賴注入的好處似芝,你應(yīng)該在你的代碼中手動嘗試一下那婉。
資料來源
https://developer.android.google.cn/training/dependency-injection?hl=zh_cn