06.依賴(lài)倒置原則介紹

06.依賴(lài)倒置原則介紹

目錄介紹

  • 01.問(wèn)題思考的分析
  • 02.學(xué)習(xí)依賴(lài)倒置目標(biāo)
  • 03.理解依賴(lài)倒置原則
  • 04.依賴(lài)倒置原則思想
  • 05.多數(shù)據(jù)庫(kù)操作案例
  • 06.用戶(hù)購(gòu)買(mǎi)家電案例
  • 07.發(fā)送消息的案例
  • 08.依賴(lài)倒置VS依賴(lài)注入
  • 09.依賴(lài)倒置的思考
  • 10.依賴(lài)倒置原則總結(jié)

01.問(wèn)題思考的分析

依賴(lài)倒置原則。單一職責(zé)原則和開(kāi)閉原則的原理比較簡(jiǎn)單,但是,想要在實(shí)踐中用好卻比較難。而今天要講到的依賴(lài)倒置原則正好相反。

這個(gè)原則用起來(lái)比較簡(jiǎn)單,但概念理解起來(lái)比較難。比如,下面這幾個(gè)問(wèn)題,你看看能否清晰地回答出來(lái):

  1. “依賴(lài)倒置”這個(gè)概念指的是“誰(shuí)跟誰(shuí)”的“什么依賴(lài)”被反轉(zhuǎn)了?“倒置”兩個(gè)字該如何理解?
  2. 經(jīng)常聽(tīng)到另外一個(gè)概念:依賴(lài)注入。這兩個(gè)概念跟“依賴(lài)倒置”有什么區(qū)別和聯(lián)系呢?它們說(shuō)的是同一個(gè)事情嗎?

02.學(xué)習(xí)依賴(lài)倒置目標(biāo)

  1. 搞懂依賴(lài)倒置原則,為什么依賴(lài)倒置是重要的,以及它如何幫助我們構(gòu)建靈活、可擴(kuò)展和可維護(hù)的軟件系統(tǒng)。
  2. 掌握依賴(lài)倒置的實(shí)現(xiàn)方式,依賴(lài)注入、工廠(chǎng)模式、策略模式等常見(jiàn)的實(shí)現(xiàn)方式,以及它們的優(yōu)缺點(diǎn)和適用場(chǎng)景。
  3. 應(yīng)用依賴(lài)倒置到實(shí)際項(xiàng)目:通過(guò)實(shí)際項(xiàng)目的練習(xí)和實(shí)踐,將依賴(lài)倒置應(yīng)用到實(shí)際的軟件開(kāi)發(fā)中。

03.理解依賴(lài)倒置原則

依賴(lài)倒置原則是面向?qū)ο笤O(shè)計(jì)的六大原則之一,它的定義是:

  1. 高層模塊不應(yīng)該依賴(lài)于低層模塊。兩者都應(yīng)該依賴(lài)于抽象。
  2. 抽象不應(yīng)該依賴(lài)于細(xì)節(jié)。細(xì)節(jié)應(yīng)該依賴(lài)于抽象。

換句話(huà)說(shuō),設(shè)計(jì)中應(yīng)當(dāng)依賴(lài)于接口或抽象類(lèi),而不是依賴(lài)于具體實(shí)現(xiàn)。這種設(shè)計(jì)有助于減少模塊之間的耦合,增加系統(tǒng)的靈活性和可維護(hù)性。

04.依賴(lài)倒置原則思想

依賴(lài)倒置原則的核心思想是面向抽象編程而不是面向具體編程。通過(guò)依賴(lài)抽象層次(如接口、抽象類(lèi)),可以讓高層模塊不依賴(lài)具體的實(shí)現(xiàn)細(xì)節(jié),從而使得系統(tǒng)更具擴(kuò)展性和靈活性。

05.多數(shù)據(jù)庫(kù)操作案例

例子1:違反依賴(lài)倒置原則。假設(shè)我們有一個(gè)UserService類(lèi),它直接依賴(lài)于MySQLDatabase類(lèi)來(lái)進(jìn)行數(shù)據(jù)操作。

class MySQLDatabase {
    public void saveUser(String username) {
        // 保存用戶(hù)到MySQL數(shù)據(jù)庫(kù)
        System.out.println("Saving " + username + " to MySQL database.");
    }
}

class UserService {
    private MySQLDatabase database = new MySQLDatabase();

    public void addUser(String username) {
        database.saveUser(username);
    }
}

在這個(gè)例子中,UserService類(lèi)直接依賴(lài)于MySQLDatabase類(lèi),這是對(duì)具體實(shí)現(xiàn)的依賴(lài),違反了依賴(lài)倒置原則。

如果我們需要將數(shù)據(jù)庫(kù)換成PostgresSQLDatabase,或者換成AliSQLDatabase,我們必須修改UserService類(lèi),這增加了系統(tǒng)的耦合度和修改的風(fēng)險(xiǎn)。

例子2:遵循依賴(lài)倒置原則??梢酝ㄟ^(guò)引入一個(gè)抽象層來(lái)遵循依賴(lài)倒置原則。

// 抽象層:定義一個(gè)接口,供不同的數(shù)據(jù)庫(kù)實(shí)現(xiàn)
interface Database {
    void saveUser(String username);
}

// 具體實(shí)現(xiàn):MySQL數(shù)據(jù)庫(kù)
class MySQLDatabase implements Database {
    public void saveUser(String username) {
        System.out.println("Saving " + username + " to MySQL database.");
    }
}

// 具體實(shí)現(xiàn):PostgreSQL數(shù)據(jù)庫(kù)
class PostgreSQLDatabase implements Database {
    public void saveUser(String username) {
        System.out.println("Saving " + username + " to PostgreSQL database.");
    }
}

// 高層模塊:UserService類(lèi)依賴(lài)于抽象接口,而不是具體實(shí)現(xiàn)
class UserService {

    private Database database;

    public UserService(Database database) {
        this.database = database;
    }

    public void addUser(String username) {
        database.saveUser(username);
    }
}

在這個(gè)例子中,UserService類(lèi)依賴(lài)于Database接口,而不是具體的數(shù)據(jù)庫(kù)實(shí)現(xiàn)。

這意味著如果我們想更換數(shù)據(jù)庫(kù),只需要傳入不同的實(shí)現(xiàn)類(lèi),而不需要修改UserService類(lèi)本身。這樣做遵循了依賴(lài)倒置原則,降低了模塊之間的耦合度。

從這個(gè)例子可知,定義數(shù)據(jù)庫(kù)抽象層供不同的數(shù)據(jù)庫(kù)實(shí)現(xiàn),然后在UserService類(lèi)依賴(lài)于抽象接口而不是具體實(shí)現(xiàn),可以充分降低代碼耦合度,可以快速拓展其他數(shù)據(jù)庫(kù)實(shí)現(xiàn)。

06.用戶(hù)購(gòu)買(mǎi)家電案例

例子1:違反依賴(lài)倒置原則。假設(shè)顧客,想要買(mǎi)冰箱,洗衣機(jī),電視,或者其他電器,它直接依賴(lài)于Customer類(lèi)來(lái)進(jìn)行數(shù)據(jù)操作。

public class Customer {
    public void buyFridge() {
        System.out.println("購(gòu)買(mǎi)冰箱");
    }

    public void buyTelevision() {
        System.out.println("購(gòu)買(mǎi)電視");
    }
}

例子2:遵循依賴(lài)倒置原則??梢酝ㄟ^(guò)引入一個(gè)抽象層來(lái)遵循依賴(lài)倒置原則。

/**
 * 商品接口
 */
public interface IGood {
    /**
     * 購(gòu)買(mǎi)商品
     */
    void buy();
}

public class Customer {
    public void buy(IGood iGood) {
        iGood.buy();
    }
}

/**
 * 冰箱商品
 */
public class FridgeGood implements IGood {
    @Override
    public void buy() {
        System.out.println("購(gòu)買(mǎi)冰箱");
    }
}

/**
 * 電視商品
 */
public class TelevisionGood implements IGood {
    @Override
    public void buy() {
        System.out.println("購(gòu)買(mǎi)電視");
    }
}

/**
 * 洗衣機(jī)商品
 */
public class WashMachineGood implements IGood {
    @Override
    public void buy() {
        System.out.println("購(gòu)買(mǎi)洗衣機(jī)");
    }
}

public class Main {
    public static void main(String[] args) {
        Customer customer = new Customer();
        customer.buy(new FridgeGood());
        customer.buy(new TelevisionGood());
        customer.buy(new WashMachineGood());
    }
}

07.發(fā)送消息的案例

例子1:違反依賴(lài)倒置原則。假設(shè)用戶(hù),可以通過(guò)郵件,短信息,信件等發(fā)送消息,下面這種就不太友好。

public class Notification {
    public void email(String cellphone, String message) {
        System.out.println("通過(guò)郵件發(fā)送消息");
    }

    public void message(String cellphone, String message) {
        System.out.println("通過(guò)短信息發(fā)送消息");
    }
    
    public void letter(String cellphone, String message) {
        System.out.println("通過(guò)信件發(fā)送消息");
    }
}

例子2:遵循依賴(lài)倒置原則??梢酝ㄟ^(guò)引入一個(gè)抽象層來(lái)遵循依賴(lài)倒置原則。

public class Notification {
  private MessageSender messageSender;
  
  public Notification(MessageSender messageSender) {
    this.messageSender = messageSender;
  }
  
  public void sendMessage(String cellphone, String message) {
    this.messageSender.send(cellphone, message);
  }
}

public interface MessageSender {
  void send(String cellphone, String message);
}

// 郵件發(fā)送類(lèi)
public class EmailSender implements MessageSender {
  @Override
  public void send(String cellphone, String message) {
    //....
  }
}

// 短信發(fā)送類(lèi)
public class SmsSender implements MessageSender {
  @Override
  public void send(String cellphone, String message) {
    //....
  }
}

// 站內(nèi)信發(fā)送類(lèi)
public class InboxSender implements MessageSender {
  @Override
  public void send(String cellphone, String message) {
    //....
  }
}

08.依賴(lài)倒置VS依賴(lài)注入

8.1 理解依賴(lài)注入

依賴(lài)注入(Dependency Injection, DI)是一種設(shè)計(jì)模式,旨在將對(duì)象的依賴(lài)關(guān)系從對(duì)象內(nèi)部轉(zhuǎn)移到外部管理,從而降低類(lèi)之間的耦合度,提高代碼的可維護(hù)性和可測(cè)試性。

8.2 依賴(lài)注入方式

依賴(lài)注入(Dependency Injection,DI)是一種通過(guò)將依賴(lài)關(guān)系從高層模塊解耦的方式,常用于實(shí)現(xiàn)依賴(lài)倒置原則。

  1. 構(gòu)造函數(shù)注入:通過(guò)在類(lèi)的構(gòu)造函數(shù)中聲明依賴(lài)參數(shù),將依賴(lài)關(guān)系通過(guò)構(gòu)造函數(shù)傳遞給類(lèi)的實(shí)例。最常見(jiàn)的依賴(lài)注入方式。
  2. Setter方法注入:通過(guò)提供一組setter方法,允許外部代碼設(shè)置依賴(lài)對(duì)象。這種方式相對(duì)于構(gòu)造函數(shù)注入更靈活,可以在對(duì)象創(chuàng)建后隨時(shí)更改依賴(lài)。
  3. 接口注入:通過(guò)在類(lèi)中定義一個(gè)接口,該接口包含用于注入依賴(lài)的方法。類(lèi)實(shí)現(xiàn)該接口,并通過(guò)接口方法接收依賴(lài)對(duì)象。這種方式相對(duì)較少使用,因?yàn)樗肓烁嗟慕涌诤头椒ā?/li>
  4. 屬性注入:通過(guò)在類(lèi)中聲明依賴(lài)對(duì)象的屬性,并提供相應(yīng)的setter方法,將依賴(lài)對(duì)象注入到屬性中。可能導(dǎo)致類(lèi)的實(shí)例在沒(méi)有依賴(lài)對(duì)象的情況下被創(chuàng)建。

8.3 兩者有何區(qū)別

依賴(lài)倒置原則(Dependency Inversion Principle,DIP)和依賴(lài)注入(Dependency Injection,DI)是面向?qū)ο笤O(shè)計(jì)中兩個(gè)相關(guān)但不同的概念。

  1. 依賴(lài)倒置原則是一種設(shè)計(jì)原則,它指導(dǎo)我們?cè)谠O(shè)計(jì)軟件時(shí)應(yīng)該依賴(lài)于抽象而不是具體實(shí)現(xiàn)。高層模塊不應(yīng)該直接依賴(lài)于低層模塊,而是通過(guò)抽象接口或基類(lèi)來(lái)進(jìn)行依賴(lài)。
  2. 依賴(lài)注入是一種實(shí)現(xiàn)依賴(lài)倒置原則的具體技術(shù),它通過(guò)將依賴(lài)關(guān)系從高層模塊解耦,將依賴(lài)對(duì)象注入到類(lèi)的實(shí)例中。依賴(lài)注入有多種方式,如構(gòu)造函數(shù)注入、setter方法注入、屬性注入等。它的目的是通過(guò)外部注入依賴(lài)對(duì)象,而不是在類(lèi)內(nèi)部創(chuàng)建或獲取依賴(lài)對(duì)象。

依賴(lài)注入是實(shí)現(xiàn)依賴(lài)倒置原則的一種常見(jiàn)方式,但并不是唯一的方式。依賴(lài)倒置原則還可以通過(guò)工廠(chǎng)模式、策略模式等其他設(shè)計(jì)模式來(lái)實(shí)現(xiàn)。

09.依賴(lài)倒置的思考

9.1 依賴(lài)倒置優(yōu)點(diǎn)

  1. 降低耦合性:高層模塊和低層模塊之間通過(guò)抽象進(jìn)行解耦,避免了對(duì)具體實(shí)現(xiàn)的依賴(lài)。
  2. 增強(qiáng)可擴(kuò)展性:通過(guò)依賴(lài)抽象,可以更容易地替換或擴(kuò)展具體實(shí)現(xiàn),而不需要修改高層模塊。
  3. 提高可維護(hù)性:模塊之間的解耦使得系統(tǒng)的維護(hù)變得更加容易,修改一個(gè)模塊的實(shí)現(xiàn)不會(huì)影響到其他模塊。

9.2 依賴(lài)倒置缺點(diǎn)

  1. 增加系統(tǒng)的復(fù)雜性:為了遵循依賴(lài)倒置原則,通常需要引入額外的抽象層次,這可能會(huì)增加系統(tǒng)的復(fù)雜性。
  2. 可能導(dǎo)致過(guò)度設(shè)計(jì):在簡(jiǎn)單系統(tǒng)中,如果過(guò)度使用依賴(lài)倒置原則,可能導(dǎo)致不必要的復(fù)雜性,造成過(guò)度設(shè)計(jì)。

9.3 在設(shè)計(jì)模式的體現(xiàn)

  1. 工廠(chǎng)模式(Factory Pattern):通過(guò)定義抽象工廠(chǎng)接口,讓具體的工廠(chǎng)類(lèi)實(shí)現(xiàn)該接口,從而將對(duì)象的創(chuàng)建過(guò)程與使用過(guò)程解耦。這樣,高層模塊只依賴(lài)于抽象工廠(chǎng)接口,而不依賴(lài)于具體的產(chǎn)品類(lèi)。這符合依賴(lài)倒置原則,使得高層模塊依賴(lài)于抽象而非具體實(shí)現(xiàn)。
  2. 觀察者模式(Observer Pattern):觀察者依賴(lài)于被觀察者的抽象接口,而不依賴(lài)于具體的被觀察者。這樣,當(dāng)被觀察者發(fā)生變化時(shí),觀察者可以接收到通知并做出相應(yīng)的處理,而不需要直接依賴(lài)于具體的被觀察者。
  3. 策略模式(Strategy Pattern):通過(guò)定義一個(gè)抽象策略接口,讓具體的策略類(lèi)實(shí)現(xiàn)該接口,從而將算法的選擇與使用解耦。高層模塊通過(guò)依賴(lài)于抽象策略接口,而不依賴(lài)于具體的策略類(lèi),實(shí)現(xiàn)了依賴(lài)倒置原則。

這些設(shè)計(jì)模式的共同點(diǎn)是,它們都通過(guò)引入抽象接口或基類(lèi),將高層模塊與具體實(shí)現(xiàn)解耦,使得高層模塊依賴(lài)于抽象而非具體實(shí)現(xiàn)。符合依賴(lài)倒置原則的設(shè)計(jì)思想。

10.依賴(lài)倒置原則總結(jié)

  1. 依賴(lài)倒置問(wèn)題思考:“依賴(lài)倒置”這個(gè)概念指的是“誰(shuí)跟誰(shuí)”的“什么依賴(lài)”被反轉(zhuǎn)了?“倒置”兩個(gè)字該如何理解?
  2. 學(xué)習(xí)該原則目標(biāo):搞懂該原則作用,掌握實(shí)現(xiàn)方式,并且應(yīng)用到實(shí)際案例項(xiàng)目中。
  3. 依賴(lài)倒置的定義:高層模塊不應(yīng)該依賴(lài)底層模塊,應(yīng)該依賴(lài)于抽象。換句話(huà)說(shuō),設(shè)計(jì)中應(yīng)當(dāng)依賴(lài)于接口或抽象類(lèi),而不是依賴(lài)于具體實(shí)現(xiàn)。
  4. 依賴(lài)倒置原則思想:通過(guò)依賴(lài)抽象層次(如接口、抽象類(lèi)),可以讓高層模塊不依賴(lài)具體的實(shí)現(xiàn)細(xì)節(jié),從而使得系統(tǒng)更具擴(kuò)展性和靈活性。
  5. 多數(shù)據(jù)庫(kù)操作案例:從這個(gè)例子可知,定義數(shù)據(jù)庫(kù)抽象層供不同的數(shù)據(jù)庫(kù)實(shí)現(xiàn),然后在UserService類(lèi)依賴(lài)于抽象接口而不是具體實(shí)現(xiàn),可以充分降低代碼耦合度,可以快速拓展其他數(shù)據(jù)庫(kù)實(shí)現(xiàn)。
  6. 依賴(lài)注入如何理解:依賴(lài)注入(Dependency Injection,DI)是一種通過(guò)將依賴(lài)關(guān)系從高層模塊解耦的方式,常用于實(shí)現(xiàn)依賴(lài)倒置原則。
  7. 依賴(lài)注入有哪些方式:1.構(gòu)造函數(shù)注入;2.Setter方法注入;3.接口注入;4.屬性注入
  8. 依賴(lài)倒置和依賴(lài)注入?yún)^(qū)別:依賴(lài)倒置原則是一種設(shè)計(jì)原則,依賴(lài)注入是一種實(shí)現(xiàn)依賴(lài)倒置原則的具體技術(shù)。
  9. 依賴(lài)倒置有哪些缺點(diǎn):為了遵循依賴(lài)倒置原則,通常需要引入額外的抽象層次,這可能會(huì)增加系統(tǒng)的復(fù)雜性。
  10. 在設(shè)計(jì)模式的體現(xiàn)有哪些:工廠(chǎng)模式,觀察者模式,策略模式,這些設(shè)計(jì)模式的共同點(diǎn)是,它們都通過(guò)引入抽象接口或基類(lèi),將高層模塊與具體實(shí)現(xiàn)解耦,使得高層模塊依賴(lài)于抽象而非具體實(shí)現(xiàn)。符合依賴(lài)倒置原則的設(shè)計(jì)思想。

11.更多內(nèi)容推薦

模塊 描述 備注
GitHub 多個(gè)YC系列開(kāi)源項(xiàng)目,包含Android組件庫(kù),以及多個(gè)案例 GitHub
博客匯總 匯聚Java,Android,C/C++,網(wǎng)絡(luò)協(xié)議,算法,編程總結(jié)等 YCBlogs
設(shè)計(jì)模式 六大設(shè)計(jì)原則,23種設(shè)計(jì)模式,設(shè)計(jì)模式案例,面向?qū)ο笏枷?/td> 設(shè)計(jì)模式
Java進(jìn)階 數(shù)據(jù)設(shè)計(jì)和原理,面向?qū)ο蠛诵乃枷?,IO,異常,線(xiàn)程和并發(fā),JVM Java高級(jí)
網(wǎng)絡(luò)協(xié)議 網(wǎng)絡(luò)實(shí)際案例,網(wǎng)絡(luò)原理和分層,Https,網(wǎng)絡(luò)請(qǐng)求,故障排查 網(wǎng)絡(luò)協(xié)議
計(jì)算機(jī)原理 計(jì)算機(jī)組成結(jié)構(gòu),框架,存儲(chǔ)器,CPU設(shè)計(jì),內(nèi)存設(shè)計(jì),指令編程原理,異常處理機(jī)制,IO操作和原理 計(jì)算機(jī)基礎(chǔ)
學(xué)習(xí)C編程 C語(yǔ)言入門(mén)級(jí)別系統(tǒng)全面的學(xué)習(xí)教程,學(xué)習(xí)三到四個(gè)綜合案例 C編程
C++編程 C++語(yǔ)言入門(mén)級(jí)別系統(tǒng)全面的教學(xué)教程,并發(fā)編程,核心原理 C++編程
算法實(shí)踐 專(zhuān)欄,數(shù)組,鏈表,棧,隊(duì)列,樹(shù),哈希,遞歸,查找,排序等 Leetcode
Android 基礎(chǔ)入門(mén),開(kāi)源庫(kù)解讀,性能優(yōu)化,F(xiàn)ramework,方案設(shè)計(jì) Android
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容