按照慣例,首先咱們來看一下依賴倒置原則的定義。html
抽象不該該依賴於細節,細節應當依賴於抽象。 換言之,要針對接口編程,而不是針對實現編程。程序員
爲何要這樣說呢?編程
由於細節具備易變性,很是的不穩定。不少時候,需求改變就會給細節帶來改變。bash
而抽象則是相對穩定的,抽象是從衆多的事物中抽取出共同的、本質性的特徵,是比較難被改變的。app
因此,咱們確定要選擇對抽象編程,而不選擇對細節編程。框架
抽象在Java中則指的是抽象類或者接口;細節則是表明着具體實現。ide
對接口編程,是寫出健壯性代碼的根本,是優秀程序員必備的基本素質。學習
一、高層模塊不該該依賴低層模塊,二者都應該依賴其抽象ui
不管是高層模塊,仍是低層模塊,所有都屬於具體的實現,屬於細節,而細節確定是不能相互依賴的。this
他們都應該依賴於抽象。
二、抽象不該該依賴細節
都不該該依賴於細節,特別是抽象更加不該該依賴細節。
三、細節應該依賴抽象
都應該依賴抽象,細節依賴抽象,抽象也依賴抽象。
一、接口方法中聲明依賴對象
package com.fanqiekt.principle.inversion;
/**
* 廚師接口
*
* @author 番茄課堂-懶人
*/
public interface IChef {
/**
* 作飯
*/
void cooking();
}
複製代碼
廚師都會作飯。
package com.fanqiekt.principle.inversion;
/**
* 四川廚師
*
* @author 番茄課堂-懶人
*/
public class SiChuanChef implements IChef {
@Override
public void cooking() {
System.out.println("四川廚師作飯,多放辣椒。");
}
}
複製代碼
廚師的實現類,川菜廚師,其中的邏輯都屬於細節,例如川菜廚師有本身作飯的套路(不能沒有辣)。
package com.fanqiekt.principle.inversion;
/**
* 山東廚師
*
* @author 番茄課堂-懶人
*/
public class ShanDongChef implements IChef {
@Override
public void cooking() {
System.out.println("山東廚師作飯,用蔥薑蒜。");
}
}
複製代碼
另外一個廚師的實現類,魯系廚師,魯菜廚師也有本身作飯的套路(善用蔥薑蒜)。
package com.fanqiekt.principle.inversion;
public interface IWaiter {
/**
* 點餐
* @param chef 指定作飯的菜系廚師
*/
void order(IChef chef);
}
複製代碼
服務員的抽象接口,服務員都須要爲客人點餐。
order(IChef chef) 方法就是接口方法中聲明依賴對象。
在接口中定義該方法,並將依賴的抽象對象做爲參數傳入。
package com.fanqiekt.principle.inversion;
/**
* 接口方法中聲明依賴對象
*
* @author 番茄課堂-懶人
*/
public class Waiter implements IWaiter{
@Override
public void order(IChef chef){
if(chef!=null) {
chef.cooking();
}
}
}
複製代碼
服務員的實現類,點餐是讓傳入的廚師去作飯。
IChef sichuanChef = new SiChuanChef();
IChef shandongChef = new ShanDongChef();
IWaiter waiter = new Waiter();
waiter.order(sichuanChef);
waiter.order(shandongChef);
複製代碼
將抽象對象做爲order方法參數。
四川廚師作飯
山東廚師作飯
複製代碼
運行結果。
每次調用方法時,都傳入依賴對象。
優勢是靈活,每次均可以傳入不一樣的依賴對象。
缺點是繁瑣,每次都須要傳入依賴對象。
二、構造方法傳遞依賴對象
package com.fanqiekt.principle.inversion;
public interface IWaiter {
/**
* 點餐
*/
void cooking();
}
複製代碼
服務員接口修改:cooking方法去掉參數。
package com.fanqiekt.principle.inversion;
/**
* 構造方法傳遞依賴對象
*
* @author 番茄課堂-懶人
*/
public class Waiter implements IWaiter {
private IChef chef;
/**
* 構造方法中傳入依賴的抽象對象
* @param chef 廚師抽象接口
*/
public Waiter(IChef chef){
this.chef = chef;
}
@Override
public void cooking(){
if(chef!=null) {
chef.cooking();
}
}
}
複製代碼
經過構造方法傳入依賴的抽象對象。
Waiter waiter1 = new Waiter(sichuanChef);
waiter1.cooking();
Waiter waiter2 = new Waiter(shandongChef);
waiter2.cooking();
複製代碼
運行看一下結果。
四川廚師作飯
山東廚師作飯
複製代碼
首次建立的時候就肯定了依賴,既是優勢又是缺點。
優勢是避免了被修改。
缺點是更換依賴,就須要從新再建立對象了。
三、Setter方法傳遞依賴對象
package com.fanqiekt.principle.inversion;
/**
* Setter方法傳遞依賴對象
*
* @author 番茄課堂-懶人
*/
public class Waiter implements IWaiter {
private IChef chef;
public void setChef(IChef chef){
this.chef = chef;
}
@Override
public void cooking(){
if(chef!=null) {
chef.cooking();
}
}
}
複製代碼
經過set方法賦值依賴對象。
Waiter plan = new Waiter();
plan.setChef(sichuanChef);
plan.cooking();
plan.setChef(shandongChef);
plan.cooking();
複製代碼
運行看一下結果。
四川廚師作飯
山東廚師作飯
複製代碼
Setter既能夠更換依賴對象,也不用每次調用方法時都傳入依賴對象。
咱們來總結一下依賴倒置原則的幾個優勢。
下降風險 依賴抽象,大大提升代碼的健壯性,風險天然而然就被下降了。
易維護易擴展 依賴抽象,纔會有框架,基於框架,擴展會更方便,維護起來也更省事。
增長開發速度 定好抽象結構就能夠並行開發了,而不用過多的被他人的進度干預。
接下來,請您欣賞依賴倒置原則的原創歌曲。
嘻哈說:依賴倒置原則
做曲:懶人
做詞:懶人
Rapper:懶人
須要依賴廚師爲我作頓美食
不用管他到底川系或者徽系
只依賴廚師接口作法多麼美麗
否則修改起來牽扯太多的話是多麼費事
set方法構造方法接口方法能夠將依賴作個配置
依賴倒置原則就是這麼回事
抽象不依賴細節 細節依賴抽象
高層不依賴低層 都應該依賴抽象
面向接口編程記住這點才能在代碼路走的夠長
易擴展易維護風險下降增長開發速度
就像番茄課堂同樣酷
複製代碼
閒來無事聽聽曲,知識已填腦中去;
學習複習新方式,頭戴耳機不小覷。
番茄課堂,學習也要酷。