不詩意的女程序媛不是好廚師~
轉(zhuǎn)載請(qǐng)注明出處,F(xiàn)rom李詩雨---https://blog.csdn.net/cjm2484836553/article/details/104449190
上篇我們學(xué)習(xí)了注解的基本知識(shí)库继,今天我們?cè)賮韺W(xué)習(xí)一下依賴注入。
當(dāng)讓我們也不是一下子就來說依賴注入,而是秉著循序漸進(jìn)的原則蒋搜,一點(diǎn)一點(diǎn)的理解册着。
那就讓我們開始吧~
1.什么是依賴(Dependency)?
通俗點(diǎn)來講 依賴 就是一種需要逃顶。
依賴是類與類之間的連接讨便,依賴關(guān)系表示一個(gè)類依賴于另一個(gè)類的定義。
【舉個(gè)栗子】:
例如一個(gè)人(Person)可以買車(Car)和房子(House),那Person類就依賴于Car類和House類以政。
public static void main(String[] args) {
Person person = new Person();
person.buy(new House());
person.buy(new Car());
}
static class Person {
//表示依賴House
public void buy(House house) {
System.out.println("買了套房");
}
//表示依賴Car
public void buy(Car car) {
System.out.println("買了輛車");
}
}
static class House {
}
static class Car {
}
嗯吶霸褒,了解了依賴是什么,接下來繼續(xù)下一個(gè)概念:依賴倒置 ~
2.什么是 依賴倒置 盈蛮?
2.0 順序依賴
有時(shí)候理解一個(gè)東西废菱,通過反面來理解也不失為一種好的方法。
那依賴倒置的反面就是不依賴倒置了抖誉,就是順序依賴了昙啄。
什么是順序依賴呢?
比如說:
人的出行依賴于車子寸五,那如果出行由自行車變成了汽車梳凛,人對(duì)應(yīng)的類就要改變;如果又變成了火車梳杏,人對(duì)應(yīng)的類又要改變韧拒。
人依賴于車子,車子一變十性,人就要跟著改變叛溢,這種就叫順序的依賴。
public class Person {
private Bike mBike;
private Car mCar;
private Train mTrain;
public Person() {
mBike = new Bike();
//mCar = new Car();// ①變---改為汽車
//mTrain = new Train();//②變---改為火車
}
public void goOut() {
System.out.println("依賴于車子要出門了~");
mBike.drive();
//mCar.drive(); //①跟著變
//mTrain.drive(); //②跟著變
}
public static void main(String... args) {
Person person = new Person();
person.goOut();
}
}
好的劲适,了解了順序的依賴楷掉,那依賴的倒置就是反過來嘍,那就讓我再來繼續(xù)看看吧~
2.1依賴倒置的定義
依賴倒置是面向?qū)ο笤O(shè)計(jì)領(lǐng)域的一種軟件設(shè)計(jì)原則霞势。
依賴倒置原則(Dependence Inversion Principle,簡(jiǎn)稱DIP)
- 核心思想:高層模塊不應(yīng)該依賴底層模塊烹植,二者都該依賴其抽象斑鸦;抽象不應(yīng)該依賴細(xì)節(jié);細(xì)節(jié)應(yīng)該依賴抽象草雕;
- 說明:高層模塊就是調(diào)用端巷屿,低層模塊就是具體實(shí)現(xiàn)類。抽象就是指接口或抽象類墩虹。細(xì)節(jié)就是實(shí)現(xiàn)類嘱巾。
- 通俗來講: 依賴倒置原則的本質(zhì)就是通過抽象(接口或抽象類)使個(gè)各類或模塊的實(shí)現(xiàn)彼此獨(dú)立,互不影響诫钓,實(shí)現(xiàn)模塊間的松耦合旬昭。
- 問題描述: 類A直接依賴類B,假如要將類A改為依賴類C菌湃,則必須通過修改類A的代碼來達(dá)成问拘。這種場(chǎng)景下,類A一般是高層模塊慢味,負(fù)責(zé)復(fù)雜的業(yè)務(wù)邏輯;類B和類C是低層模塊墅冷,負(fù)責(zé)基本的原子操作纯路;假如修改類A,會(huì)給程序帶來不必要的風(fēng)險(xiǎn)寞忿。
- 解決方案: 將類A修改為依賴接口interface驰唬,類B和類C各自實(shí)現(xiàn)接口interface,類A通過接口interface間接與類B或者類C發(fā)生聯(lián)系腔彰,則會(huì)大大降低修改類A的幾率叫编。
- 好處:依賴倒置的好處在小型項(xiàng)目中很難體現(xiàn)出來。但在大中型項(xiàng)目中可以減少需求變化引起的工作量霹抛。使并行開發(fā)更友好搓逾。
我們現(xiàn)在知道了依賴倒置原則的核心是:
- 上層模塊不應(yīng)該依賴底層模塊,它們都應(yīng)該依賴于抽象杯拐。
- 抽象不應(yīng)該依賴于細(xì)節(jié)霞篡,細(xì)節(jié)應(yīng)該依賴于抽象。
那什么是上層模塊和底層模塊 端逼,什么又是抽象和細(xì)節(jié)呢朗兵?
我們來繼續(xù)往下看~
2.2上層模塊和底層模塊
就拿一個(gè)公司來說吧,它一定有架構(gòu)的設(shè)計(jì)顶滩、有職能的劃分余掖。按照職能的重要性,自然而然就有了上下之分礁鲁。并且盐欺,隨著模塊的粒度劃分不同這種上層與底層模塊會(huì)進(jìn)行變動(dòng)赁豆,也許某一模塊相對(duì)于另外一模塊它是底層,但是相對(duì)于其他模塊它又可能是上層找田。
如圖歌憨,公司管理層就是上層,CEO 是整個(gè)事業(yè)群的上層墩衙,那么 CEO 職能之下就是底層务嫡。
然后,我們?cè)僖允聵I(yè)群為整個(gè)體系劃分模塊漆改,各個(gè)部門經(jīng)理以上部分是上層心铃,那么之下的組織都可以稱為底層。
由此挫剑,我們可以看到去扣,在一個(gè)特定體系中,上層模塊與底層模塊可以按照決策能力高低為準(zhǔn)繩進(jìn)行劃分樊破。
那么愉棱,映射到我們軟件實(shí)際開發(fā)中,一般我們也會(huì)將軟件進(jìn)行模塊劃分哲戚,比如業(yè)務(wù)層奔滑、邏輯層和數(shù)據(jù)層。
業(yè)務(wù)層中是軟件真正要進(jìn)行的操作顺少,也就是做什么朋其。
邏輯層是軟件現(xiàn)階段為了業(yè)務(wù)層的需求提供的實(shí)現(xiàn)細(xì)節(jié),也就是怎么做脆炎。
數(shù)據(jù)層指業(yè)務(wù)層和邏輯層所需要的數(shù)據(jù)模型梅猿。
因此,如前面所總結(jié)秒裕,按照決策能力的高低進(jìn)行模塊劃分袱蚓。業(yè)務(wù)層自然就處于上層模塊,邏輯層和數(shù)據(jù)層自然就歸類為底層几蜻。
2.3抽象和具體
抽象如其名字一樣癞松,是一件很抽象的事物。
抽象往往是相對(duì)于具體而言的入蛆,具體也可以被稱為細(xì)節(jié)响蓉。
比如:
1.交通工具是抽象,而公交車哨毁、單車枫甲、火車等就是具體了。
2.表演是抽象,而唱歌想幻、跳舞粱栖、小品等就是具體。
由此可見脏毯,抽象可以是物也可以是行為闹究。
在我們實(shí)際的軟件開發(fā)中,抽象有兩種形式:接口 & 抽象類食店。
/**
* Driveable 是接口渣淤,所以它是抽象
*/
public interface Driveable {
void drive();
}
/**
* 而 Bike 實(shí)現(xiàn)了接口,它被稱為具體吉嫩。
*/
public class Bike implements Driveable {
@Override
public void drive() {
System.out.println("Bike drive");
}
}
2.4依賴倒置的好處
在上述的人與車子的例子中价认,只要車子的類型一變,Person類就要跟著改動(dòng)自娩。
那有沒有一種方法能讓 Person 的變動(dòng)少一點(diǎn)呢用踩?
因?yàn)楫吘刮覀儗懙氖亲罨A(chǔ)的演示代碼,如果工程大了忙迁,代碼復(fù)雜了脐彩,Person 面對(duì)需求變動(dòng)時(shí)改動(dòng)的地方會(huì)更多。
而依賴倒置原則正好適用于解決這類情況姊扔。
下面惠奸,我們嘗試運(yùn)用依賴倒置原則對(duì)代碼進(jìn)行改造。
我們?cè)俅位仡櫹滤亩x旱眯。
上層模塊不應(yīng)該依賴底層模塊晨川,它們都應(yīng)該依賴于抽象证九。
抽象不應(yīng)該依賴于細(xì)節(jié)删豺,細(xì)節(jié)應(yīng)該依賴于抽象。
首先是上層模塊和底層模塊的拆分愧怜。
按照決策能力高低或者重要性劃分呀页,Person 屬于上層模塊,Bike拥坛、Car 和 Train 屬于底層模塊蓬蝶。
上層模塊不應(yīng)該依賴于底層模塊。
public class Person {
//======順序的依賴======
//private Bike mBike;
//private Car mCar;
//private Train mTrain;
//=====依賴倒置=====
private Driveable mDriveable;
public Person() {
//======順序的依賴======
//mBike = new Bike();
//mCar = new Car();// ①變---改為汽車
//mTrain = new Train();//②變---改為火車
//=====依賴倒置=====
mDriveable = new Train(); //依賴倒置猜惋,只需要改這里就可以了丸氛,其他地方不用修改了
}
public void goOut() {
System.out.println("依賴于車子要出門了~");
//======順序的依賴======
//mBike.drive();
//mCar.drive(); //①跟著變
//mTrain.drive(); //②跟著變
//=====依賴倒置=====
mDriveable.drive();
}
public static void main(String... args) {
Person person = new Person();
person.goOut();
}
}
可以看到,依賴倒置實(shí)質(zhì)上是面向接口編程的體現(xiàn)著摔。
好的缓窜,通過上面的講解相信你已經(jīng)漸漸體會(huì)到什么是依賴倒置了。
但是,在編碼的過程中禾锤,我們還是發(fā)現(xiàn)它的一個(gè)不足私股,那就是它不符合開閉原則。
每次我們都要修改Person類的內(nèi)部恩掷,那我們能不能再不修改Person類內(nèi)部的情況下倡鲸,來實(shí)現(xiàn)同樣的功能呢?
答案當(dāng)時(shí)是肯定的黄娘,所以接下來我們?cè)賮韺W(xué)習(xí)一下 控制反轉(zhuǎn)峭状。
3.什么是控制反轉(zhuǎn)?
控制反轉(zhuǎn)( IoC )是 Inversion of Control的縮寫寸宏,意思就是對(duì)于控制權(quán)的反轉(zhuǎn)宁炫。
上面的實(shí)例中,Person自己掌控著內(nèi)部 mDriveable 的實(shí)例化氮凝。
現(xiàn)在羔巢,我們可以更改一種方式。將 mDriveable 的實(shí)例化移到 Person 外面罩阵。
public class Person2 {
private Driveable mDriveable;
public Person2(Driveable driveable) {
this.mDriveable = driveable;
}
public void goOut() {
System.out.println("出門啦");
mDriveable.drive();
}
public static void main(String... args) {
Person2 person = new Person2(new Car());//變?yōu)榱嗽赑erson的外部進(jìn)行改變
person.goOut();
}
}
就這樣無論出行方式怎么變化竿秆,Person 這個(gè)類都不需要更改代碼了。
在上面代碼中稿壁,Person 把內(nèi)部依賴的創(chuàng)建權(quán)力移交給了 Person2這個(gè)類中的 main() 方法幽钢。也就是說 Person 只關(guān)心依賴提供的功能,但并不關(guān)心依賴的創(chuàng)建傅是。
這種思想其實(shí)就是 IoC匪燕,IoC 是一種新的設(shè)計(jì)模式,它對(duì)上層模塊與底層模塊進(jìn)行了更進(jìn)一步的解耦喧笔。控制反轉(zhuǎn)的意思是反轉(zhuǎn)了上層模塊對(duì)于底層模塊的依賴控制帽驯。
好了,學(xué)習(xí)了這么多的概念书闸,現(xiàn)在我們終于有了一定的基礎(chǔ)來學(xué)習(xí)依賴注入了~
4.什么是依賴注入
依賴注入尼变,也經(jīng)常被簡(jiǎn)稱為 DI,其實(shí)在上一節(jié)中浆劲,我們已經(jīng)見到了它的身影嫌术。它是一種實(shí)現(xiàn) IoC 的手段。什么意思呢牌借?
為了不因?yàn)橐蕾噷?shí)現(xiàn)的變動(dòng)而去修改 Person度气,也就是說以可能在 Driveable 實(shí)現(xiàn)類的改變下不改動(dòng) Person 這個(gè)類的代碼,盡可能減少兩者之間的耦合膨报。我們需要采用上一節(jié)介紹的 IoC 模式來進(jìn)行改寫代碼磷籍。
這個(gè)需要我們移交出對(duì)于依賴實(shí)例化的控制權(quán)哲虾,那么依賴怎么辦?Person 無法實(shí)例化依賴了择示,它就需要在外部(IoC 容器)賦值給它束凑,這個(gè)賦值的動(dòng)作有個(gè)專門的術(shù)語叫做注入(injection),需要注意的是在 IoC 概念中栅盲,這個(gè)注入依賴的地方被稱為 IoC 容器汪诉,但在依賴注入概念中,一般被稱為注射器 (injector)谈秫。
表達(dá)通俗一點(diǎn)就是:我不想自己實(shí)例化依賴扒寄,你(injector)創(chuàng)建它們,然后在合適的時(shí)候注入給我拟烫。
實(shí)現(xiàn)依賴注入有 3 種方式:
- 構(gòu)造函數(shù)中注入
- setter 方式注入
- 接口注入
/**
* 接口方式注入
* 接口的存在该编,表明了一種依賴配置的能力。
*/
public interface DepedencySetter {
void set(Driveable driveable);
}
public class Person2 implements DepedencySetter {
//接口方式注入
@Override
public void set(Driveable driveable) {
this.mDriveable = mDriveable;
}
private Driveable mDriveable;
//構(gòu)造函數(shù)注入
public Person2(Driveable driveable) {
this.mDriveable = driveable;
}
//setter 方式注入
public void setDriveable(Driveable mDriveable) {
this.mDriveable = mDriveable;
}
public void goOut() {
System.out.println("出門啦");
mDriveable.drive();
}
public static void main(String... args) {
Person2 person = new Person2(new Car());
person.goOut();
}
}
所以硕淑,依賴注入大家要記住的重點(diǎn)就是 : 依賴注入是實(shí)現(xiàn)控制反轉(zhuǎn)的手段课竣。
積累點(diǎn)滴,做好自己~