日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

依赖倒置原则(Dependecy-Inversion Principle)

發布時間:2023/12/20 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 依赖倒置原则(Dependecy-Inversion Principle) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

依賴倒置原則(Dependence Inversion Principle,DIP)的原始定義:

  • 高層模塊不應該依賴底層模塊,兩者都應該依賴其抽象;
  • 抽象不應該依賴細節;
  • 細節應該依賴抽象。

抽象:即抽象類或接口,兩者是不能夠實例化的。

細節:即具體的實現類,實現接口或者繼承抽象類所產生的類,兩者可以通過關鍵字new直接被實例化。

依賴倒置原則在Java語言中的表現是:

  • 模塊間的依賴通過抽象發生,實現類之間不發生直接的依賴關系,其依賴關系是通過接口或者抽象類產生的;
  • 接口或抽象類不依賴于實現類;
  • 實現類依賴接口或抽象類。

一、什么是依賴倒置原則
一種表述:
抽象不應當依賴于細節;細節應當依賴于抽象。

另一種表述:
要針對接口編程,不要針對實現編程。

針對接口編程的意思就是說,應當使用Java接口和抽象Java類進行變量的類型聲明、參量的類型聲明、方法的返回類型聲明,以及數據類型的轉換等。

不要針對實現編程的意思就是說,不應當使用具體Java類進行變量的類型聲明、參量的類型聲明、方法的返回類型聲明,以及數據類型的轉換等。

其核心思想是:依賴于抽象。具體而言就是高層模塊不依賴于底層模塊,二者都同依賴于抽象;抽象不依賴于具體,具體依賴于抽象。

高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象。

(High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.)

高層模塊不應該依賴低層模塊,即高層模塊應該持有抽象類或接口的引用,而不應該持有某一具體實現類的引用。

抽象不應該依賴細節,即接口或抽象類不應該持有某一具體實現類的引用,而應該持有此類所繼承抽象類或所實現接口的引用。

細節應該依賴抽象,即實現類也應該持有抽象類或接口的引用,而不應該持有某一具體實現類的引用。

我們知道,依賴一定會存在于類與類、模塊與模塊之間。當兩個模塊之間存在緊密的耦合關系時,最好的方法就是分離接口和實現:在依賴之間定義一個抽象的接口使得高層模塊調用接口,而底層模塊實現接口的定義,以此來有效控制耦合關系,達到依賴于抽象的設計目標。

抽象的穩定性決定了系統的穩定性,因為抽象是不變的,依賴于抽象是面向對象設計的精髓,也是依賴倒置原則的核心。

依賴于抽象是一個通用的原則,而某些時候依賴于細節則是在所難免的,必須權衡在抽象和具體之間的取舍,方法不是一層不變的。依賴于抽象,就是對接口編程,不要對實現編程。

依賴倒置原則基于這樣一個事實:相對于細節的多變性,抽象的東西要穩定的多。以抽象為基礎搭建起來的架構比以細節為基礎搭建起來的架構要穩定的多。在java中,抽象指的是接口或者抽象類,細節就是具體的實現類,使用接口或者抽象類的目的是制定好規范和契約,而不去涉及任何具體的操作,把展現細節的任務交給他們的實現類去完成。

依賴倒置原則的核心思想是面向接口編程。

該原則規定:

  • 高層次的模塊不應該依賴于低層次的模塊,兩者都應該依賴于抽象接口
  • 抽象接口不應該依賴于具體實現。而具體實現則應該依賴于抽象接口。
  • 該原則顛倒了一部分人對于面向對象設計的認識方式。如高層次和低層次對象都應該依賴于相同的抽象接口。

    應用依賴反轉原則同樣被認為是應用了[適配器模式],例如:高層的類定義了它自己的適配器接口(高層類所依賴的抽象接口)。被適配的對象同樣依賴于適配器接口的抽象(這是當然的,因為它實現了這個接口),同時它的實現則可以使用它自身所在低層模塊的代碼。通過這種方式,高層組件則不依賴于低層組件,因為它(高層組件)僅間接的通過調用適配器接口多態方法使用了低層組件,而這些多態方法則是由被適配對象以及它的低層模塊所實現的。

    依賴倒置與依賴正置

    依賴正置就是類間的依賴是實實在在的實現類間的依賴,也就是面向實現編程,這也是正常人的思維方式,我要開奔馳車就依賴奔馳車,我要使用筆記本電腦就直接依賴筆記本電腦,而編寫程序需要的是對現實世界的事物進行抽象,抽象的結構就是有了抽象類和接口,然后我們根據系統設計的需要產生了抽象間的依賴,代替了人們傳統思維中的事物間的依賴,“倒置”就是從這里產生的。

    依賴注入
    依賴注入就是將實例變量傳入到一個對象中去(Dependency injection means giving an object its instance variables)。
    什么是依賴
    如果在 Class A 中,有 Class B 的實例,則稱 Class A 對 Class B 有一個依賴。例如下面類 Human 中用到一個 Father 對象,我們就說類 Human 對類 Father 有一個依賴。
    依賴注入
    依賴注入是這樣的一種行為,在類Car中不主動創建GasEnergy的對象,而是通過外部傳入GasEnergy對象形式來設置依賴。 常用的依賴注入有如下三種方式

    構造器注入
    將需要的依賴作為構造方法的參數傳遞完成依賴注入。

    Setter方法注入
    增加setter方法,參數為需要注入的依賴亦可完成依賴注入。

    接口注入
    接口注入,聞其名不言而喻,就是為依賴注入創建一套接口,依賴作為參數傳入,通過調用統一的接口完成對具體實現的依賴注入。

    接口注入和setter方法注入類似,不同的是接口注入使用了統一的方法來完成注入,而setter方法注入的方法名稱相對比較隨意。

    在實現依賴倒轉原則時,我們需要針對抽象層編程,而將具體類的對象通過依賴注入(DependencyInjection, DI)的方式注入到其他對象中,依賴注入是指當一個對象要與其他對象發生依賴關系時,通過抽象來注入所依賴的對象。常用的注入方式有三種,分別是:構造注入,設值注入(Setter注入)和接口注入。構造注入是指通過構造函數來傳入具體類的對象,設值注入是指通過Setter方法來傳入具體類的對象,而接口注入是指通過在接口中聲明的業務方法來傳入具體類的對象。這些方法在定義時使用的是抽象類型,在運行時再傳入具體類型的對象,由子類對象來覆蓋父類對象。

    依賴注入

  • 依賴 如果在 Class A 中,有 Class B 的實例,則稱 Class A 對 Class B 有一個依賴。例如下面類 Human 中用到一個 Father 對象,我們就說類 Human 對類 Father 有一個依賴。
  • public class Human {...Father father;...public Human() {father = new Father();} }

    仔細看這段代碼我們會發現存在一些問題:
    (1). 如果現在要改變 father 生成方式,如需要用new Father(String name)初始化 father,需要修改 Human 代碼;
    (2). 如果想測試不同 Father 對象對 Human 的影響很困難,因為 father 的初始化被寫死在了 Human 的構造函數中;
    (3). 如果new Father()過程非常緩慢,單測時我們希望用已經初始化好的 father 對象 Mock 掉這個過程也很困難。

  • 依賴注入 上面將依賴在構造函數中直接初始化是一種 Hard init 方式,弊端在于兩個類不夠獨立,不方便測試。我們還有另外一種 Init 方式,如下:
  • public class Human {...Father father;...public Human(Father father) {this.father = father;} }

    上面代碼中,我們將 father 對象作為構造函數的一個參數傳入。在調用 Human 的構造方法之前外部就已經初始化好了 Father 對象。像這種非自己主動初始化依賴,而通過外部來傳入依賴的方式,我們就稱為依賴注入
    現在我們發現上面 1 中存在的兩個問題都很好解決了,簡單的說依賴注入主要有兩個好處:
    (1). 解耦,將依賴之間解耦。
    (2). 因為已經解耦,所以方便做單元測試,尤其是 Mock 測試。

    3、什么是倒置

    到了這里,我們對依賴倒置原則的“依賴”就很好理解了,但是什么是“倒置”呢。是這樣子的,剛開始按照正常人的一般思維方式,我想吃香蕉就是吃香蕉,想吃蘋果就吃蘋果,編程也是這樣,都是按照面向實現的思維方式來設計。而現在要倒置思維,提取公共的抽象,面向接口(抽象類)編程。不再依賴于具體實現了,而是依賴于接口或抽象類,這就是依賴的思維方式“倒置”了。

    4、依賴的三種實現方式

    對象的依賴關系有三種方式來傳遞:

    //人接口 public interface People {public void eat(Fruit fruit);//人都有吃的方法,不然都餓死了 } //水果接口 public interface Fruit {public String getName();//水果都是有名字的 } //具體Jim人類 public class Jim implements People{public void eat(Fruit fruit){System.out.println("Jim eat " + fruit.getName());} } //具體蘋果類 public class Apple implements Fruit{public String getName(){return "apple";} } //具體香蕉類 public class Banana implements Fruit{public String getName(){return "banana";} } public class Client {public static void main(String[] args) {People jim = new Jim();Fruit apple = new Apple();Fruit Banana = new Banana(); //這里符合了里氏替換原則jim.eat(apple);jim.eat(Banana);} }

    Client類是復雜的業務邏輯,屬于高層模塊,而People和Fruit是原子模塊,屬于低層模塊。Client依賴于抽象的People和Fruit接口,這就做到了:高層模塊不應該依賴低層模塊,兩者都應該依賴于抽象(抽象類或接口)。
    Client不僅依賴于接口Fruit,還依賴于具體的實現Apple了。

    People和Fruit接口與各自的實現類沒有關系,增加實現類不會影響接口,這就做到了:抽象(抽象類或接口)不應該依賴于細節(具體實現類)。

    Jim、Apple、Banana實現類都要去實現各自的接口所定義的抽象方法,所以是依賴于接口的。這就做到了:細節(具體實現類)應該依賴抽象。

    “依賴于抽象(或者叫接口)”這個說法是對的,只是這段代碼Fruit apple = new Apple();不能去new具體的實現,應該用工廠方法來生產出具體的實現,這樣就可以做到只依賴于接口了。

    接口方法中聲明依賴對象。就是我們上面代碼所展示的那樣。
    而接口注入是指通過在接口中聲明的業務方法來傳入具體類的對象。這些方法在定義時使用的是抽象類型,在運行時再傳入具體類型的對象,由子類對象來覆蓋父類對象。

    構造方法傳遞依賴對象。在構造函數中的需要傳遞的參數是抽象類或接口的方式實現。代碼如下:

    //具體Jim人類 public class Jim implements People{private Fruit fruit;public Jim(Fruit fruit){//構造方法傳遞依賴對象this.fruit = fruit;}public void eat(Fruit fruit){System.out.println("Jim eat " + this.fruit.getName());} }

    Setter方法傳遞依賴對象。在我們設置的setXXX方法中的參數為抽象類或接口,來實現傳遞依賴對象。代碼如下:

    //具體Jim人類 public class Jim implements People{private Fruit fruit;public void setFruit(Fruit fruit){//setter方式傳遞依賴對象this.fruit = fruit;}public void eat(){System.out.println("Jim eat " + this.fruit.getName());} }

    5、優點

    從上面的代碼修改過程中,我們可以看到由于類之間松耦合的設計,面向接口編程依賴抽象而不依賴細節,所以在修改某個類的代碼時,不會牽涉到其他類的修改,顯著降低系統風險,提高系統健壯性。

    還有一個優點是,在我們實際項目開發中,都是多人團隊協作,每人負責某一模塊。比如一個人負責開發People模塊,一人負責開發Fruit模塊,如果未采用依賴倒置原則,沒有提取抽象,那么開發People模塊的人必須等Fruit模塊開發完成后自己才能開發,否則編譯都無法通過,這就是單線程的開發。為了能夠兩人并行開發,設計時遵循依賴倒置原則,提取抽象,就可以大大提高開發進度。

    采用依賴倒置原則可以減少類間的耦合性,提高系統的穩定性,降低并行開發引起的風險,提高代碼的可讀性和可維護性。

    6、總結

    依賴倒置原則實際上就是要求“面向接口編程”。

    說到底,依賴倒置原則的核心就是面向接口編程的思想,盡量對每個實現類都提取抽象和公共接口形成接口或抽象類,依賴于抽象而不要依賴于具體實現。依賴倒置原則的本質其實就是通過抽象(抽象類或接口)使各個類或模塊的實現彼此獨立,不相互影響,實現模塊間的松耦合。但是這個原則也是6個設計原則中最難以實現的了,如果沒有實現這個原則,那么也就意味著開閉原則(對擴展開放,對修改關閉)也無法實現。

    首先要明白所有原則都是為了達到面向對象設計的可擴展可復用可維護性而出現的…… 開閉原則是目的:一個已有的代碼模塊,需要很容易增加新的擴展功能(可擴展性),這個已有模塊需要是對外開放的;為了使已有模塊可以復用(可復用性),已有模塊需要是獨立的(單一職責,高內聚,不與其他模塊耦合在一起),同時為了方便維護(可維護性),已有模塊最好不要對原有代碼進行修改,也就是需要是對內封閉的;實現了開閉原則的設計,就達到面向對象設計可擴展可復用可維護性的目的,所以說開閉原則是目的! 里氏代換原則是基礎:通過針對抽象基類編程(業務邏輯關系的建立),具體運行時代換具體子類對象執行,可以達到開閉原則的目的,該實現過程就是里氏代換原則定義本身,所以說里氏代換原則是理論基礎! 依賴倒轉原則是手段:牛人們總結了實現里氏代換原則的方法,抽象不依賴于細節,細節應該依賴于抽象的依賴倒轉原則。具體就是變量、參數、方法返回、數據類型轉換等都要用抽象定義聲明,再通過依賴注入(構造注入、設值注入和接口注入)的方式將具體對象注入到有依賴關系的對象中。所以說依賴倒轉原則是實現目的手段!

    開閉原則是目標,里氏代換原則是基礎,依賴倒轉原則是手段; 第一要明確,所有的原則都是為了實現面向對象設計的可擴展性,可復用性,可維護性(對原有代碼不要進行修改)而定義的;一個已有的代碼模塊,需要實現可擴展性,需要對外保持開放;為了實現可復用性,需要保持獨立(單一職責,高內聚,低耦合);為了實現可維護性,需要對內封閉(對已有代碼模塊不要進行修改)。 開閉原則(Open-Closed Principle, OCP):一個軟件實體應當對擴展開放(可擴展性),對修改關閉(可維護性)。即軟件實體應盡量在不修改原有代碼的情況下進行擴展。所以說開閉原則是目的。 第二,為了實現開閉原則(可擴展性,可維護性和復用性)在最初就需要對代碼模塊進行抽象化設計(抽象化是實現開閉原則的關鍵);面向對象思想中的抽象化是指把現實中一類具有相同屬性,行為的事物歸類的方法。 抽象 ---對同一類對象的共同屬性和行為進行概括,形成類。 有:數據抽象(屬性或狀態)、代碼抽象(某類對象的共有的行為特征或功能)。抽象的實現是:類 大牛們就想在Java、C#等編程語言中,可以為系統定義一個相對穩定的抽象層,而將不同的實現行為移至具體的實現層中完成。在很多面向對象編程語言中都提供了接口、抽象類等機制,可以通過它們定義系統的抽象層,再通過具體類來進行擴展。如果需要修改系統的行為,無須對抽象層進行任何改動,只需要增加新的具體類來實現新的業務功能即可,實現在不修改已有代碼的基礎上擴展系統的功能,達到開閉原則的要求。具體操作作:通過針對抽象基類編程(業務邏輯關系的建立),具體運行時代換具體子類對象執行,可以達到開閉原則的目的,該實現過程就是里氏代換原則定義本身,所以說里氏代換原則是理論基礎! 通過里氏代換原則的操操作過程,大牛們總結出抽象不依賴于細節,細節應該依賴于抽象的依賴倒轉原則。具體就是變量、參數、方法返回、數據類型轉換等都要用抽象定義聲明,再通過依賴注入(構造注入、設值注入和接口注入)或依賴獲取的方式將具體對象注入到有依賴關系的對象中。所以說依賴倒轉原則是實現目的手段! 在實現針對抽象編程的實踐中總結出接口隔離原則(Interface Segregation Principle, ISP):使用多個專門的接口,而不使用單一的總接口,即客戶端不應該依賴那些它不需要的接口。

    使用依賴倒轉原則的編程方式+里氏轉換的約束=開閉原則

    本質:

    依賴倒置原則的本質就是通過抽象(接口或者抽象類)使各個類或模型的實現彼此獨立,不互相影響,實現模塊間的松耦合。

    規則:

    每個類盡量都有接口或抽象類,或者抽象類和接口兩者都具備;
    變量的表面類型盡量是接口或者抽象類;
    任何類都不應該從具體類派生;
    盡量不要覆寫基類的非抽象方法;
    結合里氏替換原則使用。

    總結

    以上是生活随笔為你收集整理的依赖倒置原则(Dependecy-Inversion Principle)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。