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):
- “依賴(lài)倒置”這個(gè)概念指的是“誰(shuí)跟誰(shuí)”的“什么依賴(lài)”被反轉(zhuǎn)了?“倒置”兩個(gè)字該如何理解?
- 經(jīng)常聽(tīng)到另外一個(gè)概念:依賴(lài)注入。這兩個(gè)概念跟“依賴(lài)倒置”有什么區(qū)別和聯(lián)系呢?它們說(shuō)的是同一個(gè)事情嗎?
02.學(xué)習(xí)依賴(lài)倒置目標(biāo)
- 搞懂依賴(lài)倒置原則,為什么依賴(lài)倒置是重要的,以及它如何幫助我們構(gòu)建靈活、可擴(kuò)展和可維護(hù)的軟件系統(tǒng)。
- 掌握依賴(lài)倒置的實(shí)現(xiàn)方式,依賴(lài)注入、工廠(chǎng)模式、策略模式等常見(jiàn)的實(shí)現(xiàn)方式,以及它們的優(yōu)缺點(diǎn)和適用場(chǎng)景。
- 應(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ì)的六大原則之一,它的定義是:
- 高層模塊不應(yīng)該依賴(lài)于低層模塊。兩者都應(yīng)該依賴(lài)于抽象。
- 抽象不應(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)倒置原則。
- 構(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)注入方式。
- Setter方法注入:通過(guò)提供一組setter方法,允許外部代碼設(shè)置依賴(lài)對(duì)象。這種方式相對(duì)于構(gòu)造函數(shù)注入更靈活,可以在對(duì)象創(chuàng)建后隨時(shí)更改依賴(lài)。
- 接口注入:通過(guò)在類(lèi)中定義一個(gè)接口,該接口包含用于注入依賴(lài)的方法。類(lèi)實(shí)現(xiàn)該接口,并通過(guò)接口方法接收依賴(lài)對(duì)象。這種方式相對(duì)較少使用,因?yàn)樗肓烁嗟慕涌诤头椒ā?/li>
- 屬性注入:通過(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)但不同的概念。
- 依賴(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)。
- 依賴(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)
- 降低耦合性:高層模塊和低層模塊之間通過(guò)抽象進(jìn)行解耦,避免了對(duì)具體實(shí)現(xiàn)的依賴(lài)。
- 增強(qiáng)可擴(kuò)展性:通過(guò)依賴(lài)抽象,可以更容易地替換或擴(kuò)展具體實(shí)現(xiàn),而不需要修改高層模塊。
- 提高可維護(hù)性:模塊之間的解耦使得系統(tǒng)的維護(hù)變得更加容易,修改一個(gè)模塊的實(shí)現(xiàn)不會(huì)影響到其他模塊。
9.2 依賴(lài)倒置缺點(diǎn)
- 增加系統(tǒng)的復(fù)雜性:為了遵循依賴(lài)倒置原則,通常需要引入額外的抽象層次,這可能會(huì)增加系統(tǒng)的復(fù)雜性。
- 可能導(dǎo)致過(guò)度設(shè)計(jì):在簡(jiǎn)單系統(tǒng)中,如果過(guò)度使用依賴(lài)倒置原則,可能導(dǎo)致不必要的復(fù)雜性,造成過(guò)度設(shè)計(jì)。
9.3 在設(shè)計(jì)模式的體現(xiàn)
- 工廠(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)。
- 觀察者模式(Observer Pattern):觀察者依賴(lài)于被觀察者的抽象接口,而不依賴(lài)于具體的被觀察者。這樣,當(dāng)被觀察者發(fā)生變化時(shí),觀察者可以接收到通知并做出相應(yīng)的處理,而不需要直接依賴(lài)于具體的被觀察者。
- 策略模式(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é)
- 依賴(lài)倒置問(wèn)題思考:“依賴(lài)倒置”這個(gè)概念指的是“誰(shuí)跟誰(shuí)”的“什么依賴(lài)”被反轉(zhuǎn)了?“倒置”兩個(gè)字該如何理解?
- 學(xué)習(xí)該原則目標(biāo):搞懂該原則作用,掌握實(shí)現(xiàn)方式,并且應(yīng)用到實(shí)際案例項(xiàng)目中。
- 依賴(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)。
- 依賴(lài)倒置原則思想:通過(guò)依賴(lài)抽象層次(如接口、抽象類(lèi)),可以讓高層模塊不依賴(lài)具體的實(shí)現(xiàn)細(xì)節(jié),從而使得系統(tǒng)更具擴(kuò)展性和靈活性。
- 多數(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)。
- 依賴(lài)注入如何理解:依賴(lài)注入(Dependency Injection,DI)是一種通過(guò)將依賴(lài)關(guān)系從高層模塊解耦的方式,常用于實(shí)現(xiàn)依賴(lài)倒置原則。
- 依賴(lài)注入有哪些方式:1.構(gòu)造函數(shù)注入;2.Setter方法注入;3.接口注入;4.屬性注入
- 依賴(lài)倒置和依賴(lài)注入?yún)^(qū)別:依賴(lài)倒置原則是一種設(shè)計(jì)原則,依賴(lài)注入是一種實(shí)現(xiàn)依賴(lài)倒置原則的具體技術(shù)。
- 依賴(lài)倒置有哪些缺點(diǎn):為了遵循依賴(lài)倒置原則,通常需要引入額外的抽象層次,這可能會(huì)增加系統(tǒng)的復(fù)雜性。
- 在設(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 |