05.接口隔離原則介紹

05.接口隔離原則介紹

目錄介紹

  • 01.問(wèn)題思考的分析
  • 02.學(xué)習(xí)接口隔離目標(biāo)
  • 03.理解接口隔離原則
  • 04.接口隔離原則思想
  • 05.一組API接口集合案例
  • 06.單個(gè)API接口或函數(shù)
  • 07.總結(jié)接口隔離原則
  • 08.思考一道課后題
  • 09.接口隔離原則總結(jié)

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

什么叫作接口隔離法則,它和面向?qū)ο笾械慕涌谟泻螀^(qū)別?在哪些場(chǎng)景需要注意接口隔離原則?

02.學(xué)習(xí)接口隔離目標(biāo)

學(xué)習(xí)了 SOLID 原則中的單一職責(zé)原則、開(kāi)閉原則和里式替換原則,今天我們學(xué)習(xí)接口隔離原則。它對(duì)應(yīng) SOLID 中的英文字母“I”。

對(duì)于這個(gè)原則,最關(guān)鍵就是理解其中“接口”的含義。那針對(duì)“接口”,不同的理解方式,對(duì)應(yīng)在原則上也有不同的解讀方式。

除此之外,接口隔離原則跟我們之前講到的單一職責(zé)原則還有點(diǎn)兒類似,所以今天我也會(huì)具體講一下它們之間的區(qū)別和聯(lián)系。

03.理解接口隔離原則

接口隔離原則的英文翻譯是“ Interface Segregation Principle”,縮寫(xiě)為 ISP。 Robert Martin 在 SOLID 原則中是這樣定義它的:“Clients should not be forced to depend upon interfaces that they do not use?!?/p>

直譯成中文的話就是:客戶端不應(yīng)該強(qiáng)迫依賴它不需要的接口。其中的“客戶端”,可以理解為接口的調(diào)用者或者使用者。

簡(jiǎn)單來(lái)說(shuō),這個(gè)原則鼓勵(lì)將龐大而臃腫的接口拆分為更小、更具體的接口,以便客戶端只需依賴它們所需的接口

實(shí)際上,“接口”這個(gè)名詞可以用在很多場(chǎng)合中。在軟件開(kāi)發(fā)中,我們既可以把它看作一組抽象的約定,也可以具體指系統(tǒng)與系統(tǒng)之間的 API 接口,還可以特指面向?qū)ο缶幊陶Z(yǔ)言中的接口等。

理解接口隔離原則的關(guān)鍵,就是理解其中的“接口”二字。在這條原則中,我們可以把“接口”理解為下面三種東西:

  1. 一組 API 接口集合
  2. 單個(gè) API 接口或函數(shù)
  3. OOP 中的接口概念

這些接口定義了類或模塊與外部世界的交互方式。接口可以是抽象類、接口(interface)或者具體類中的公共方法。它們定義了類或模塊的行為和功能,供其他類或模塊進(jìn)行調(diào)用和使用。

04.接口隔離原則思想

4.1 先說(shuō)下遇到的問(wèn)題

在早期的軟件開(kāi)發(fā)中,常常使用大而全的接口來(lái)定義類之間的交互方式。這些接口通常包含了各種方法,無(wú)論是否被實(shí)際使用。這種設(shè)計(jì)方式存在以下問(wèn)題:

  1. 接口臃腫:大而全的接口會(huì)包含很多方法,其中有些方法對(duì)于某些類或模塊來(lái)說(shuō)是不必要的,導(dǎo)致接口冗余和龐大。
  2. 強(qiáng)迫實(shí)現(xiàn):當(dāng)一個(gè)類實(shí)現(xiàn)一個(gè)接口時(shí),它必須實(shí)現(xiàn)接口中的所有方法,即使某些方法對(duì)于該類來(lái)說(shuō)是無(wú)意義的。這導(dǎo)致了冗余的實(shí)現(xiàn)代碼。
  3. 脆弱性:當(dāng)接口發(fā)生變化時(shí),所有實(shí)現(xiàn)該接口的類都需要進(jìn)行相應(yīng)的修改,即使這些變化對(duì)于某些類來(lái)說(shuō)是不相關(guān)的。這增加了系統(tǒng)的耦合性和維護(hù)成本。

4.2 設(shè)計(jì)這原則的思想

接口隔離原則的核心思想是:

  1. 接口要小而專一:接口不應(yīng)該包含太多功能,應(yīng)該拆分成多個(gè)小接口,每個(gè)接口只定義客戶端需要的方法。
  2. 避免臃腫接口:如果接口定義了過(guò)多的方法,某些客戶端可能只需要其中一部分功能,這會(huì)導(dǎo)致不必要的依賴,增加代碼的復(fù)雜性。

理解接口隔離原則的關(guān)鍵點(diǎn)如下,用更加通俗的話來(lái)理解:

  1. 接口應(yīng)該精簡(jiǎn):接口應(yīng)該只包含客戶端所需的方法,而不應(yīng)該強(qiáng)迫客戶端實(shí)現(xiàn)它們不需要的方法。這樣可以避免客戶端因?yàn)閷?shí)現(xiàn)不需要的方法而產(chǎn)生冗余代碼。
  2. 接口應(yīng)該獨(dú)立:接口應(yīng)該獨(dú)立于具體的實(shí)現(xiàn)細(xì)節(jié),以便在不影響客戶端的情況下進(jìn)行修改和擴(kuò)展。這樣可以提高系統(tǒng)的靈活性和可維護(hù)性。
  3. 接口應(yīng)該可擴(kuò)展:接口應(yīng)該容易擴(kuò)展,以便在需要添加新功能時(shí)能夠方便地進(jìn)行修改。這樣可以避免對(duì)現(xiàn)有接口進(jìn)行破壞性的修改。

通俗地說(shuō),一個(gè)接口只應(yīng)該包含客戶端真正需要的功能,其他無(wú)關(guān)的功能應(yīng)該分離到不同的接口中。

4.3 實(shí)現(xiàn)接口隔離原則

  1. 拆分大接口:將大而全的接口拆分為更小、更具體的接口,每個(gè)接口只包含相關(guān)的方法。這樣可以避免類實(shí)現(xiàn)不需要的方法,減少接口的冗余和龐大。
  2. 定義細(xì)化接口:根據(jù)不同的使用場(chǎng)景,定義細(xì)化的接口,以滿足各個(gè)類或模塊的特定需求。每個(gè)類只需實(shí)現(xiàn)其所需的接口,避免了強(qiáng)迫實(shí)現(xiàn)不相關(guān)的方法。
  3. 接口繼承:通過(guò)接口繼承,將通用的方法定義在父接口中,然后派生出更具體的子接口。
  4. 接口組合:使用接口組合的方式,將多個(gè)小接口組合成一個(gè)更大的接口。這樣,類可以根據(jù)需要選擇性地實(shí)現(xiàn)組合接口中的方法,避免了實(shí)現(xiàn)不需要的方法。
  5. 依賴注入:通過(guò)依賴注入的方式,將類所依賴的接口作為參數(shù)傳遞給類的構(gòu)造函數(shù)或方法。這樣,類只需依賴其所需的接口,而不需要依賴不相關(guān)的接口。

05.一組API接口集合案例

5.1 錯(cuò)誤示范案例

假設(shè)我們?cè)陔娚滔到y(tǒng)中設(shè)計(jì)了一個(gè)用戶操作接口UserOperations,包含了用戶的所有操作:

interface UserOperations {
    void createOrder();
    void cancelOrder();
    void browseProducts();
    void manageAccount();
    void applyDiscount();
}

在這個(gè)設(shè)計(jì)中,所有的用戶操作都集中在一個(gè)接口中,但并不是所有用戶都需要所有這些操作。

比如,對(duì)于普通用戶,可能只需要瀏覽商品、創(chuàng)建訂單和管理賬戶的功能,而對(duì)于管理員用戶,則可能更關(guān)注于應(yīng)用折扣和訂單管理的功能。

這種設(shè)計(jì)違反了接口隔離原則,導(dǎo)致客戶端依賴了很多不需要的方法,增加了系統(tǒng)的復(fù)雜性和維護(hù)成本。

5.2 正確示范案例

為了遵循接口隔離原則,我們可以將UserOperations接口拆分成多個(gè)更小、更專一的接口:

 interface OrderOperations {
    void createOrder();
    void cancelOrder();
}

interface ProductOperations {
    void browseProducts();
}

interface AccountOperations {
    void manageAccount();
}

interface AdminOperations {
    void applyDiscount();
}

然后根據(jù)不同的用戶類型,實(shí)現(xiàn)各自所需的接口:

class NormalUser implements OrderOperations, ProductOperations, AccountOperations {
    @Override
    public void createOrder() {
        // 創(chuàng)建訂單邏輯
    }

    @Override
    public void cancelOrder() {
        // 取消訂單邏輯
    }

    @Override
    public void browseProducts() {
        // 瀏覽商品邏輯
    }

    @Override
    public void manageAccount() {
        // 管理賬戶邏輯
    }
}

class AdminUser implements AdminOperations, OrderOperations {
    @Override
    public void applyDiscount() {
        // 應(yīng)用折扣邏輯
    }

    @Override
    public void createOrder() {
        // 創(chuàng)建訂單邏輯
    }

    @Override
    public void cancelOrder() {
        // 取消訂單邏輯
    }
}

這種設(shè)計(jì)方式將接口職責(zé)進(jìn)行了合理的劃分,使得每個(gè)接口只包含客戶端真正需要的方法,符合接口隔離原則的要求。

在電商系統(tǒng)中,遵循接口隔離原則可以確保用戶操作接口不會(huì)變得臃腫,每個(gè)用戶類型只依賴于它們需要的功能接口,從而避免了不必要的依賴和實(shí)現(xiàn)負(fù)擔(dān)。

5.3 電商案例總結(jié)

問(wèn)題:接口臃腫

在電商系統(tǒng)中,如果我們?yōu)橛脩粼O(shè)計(jì)了一個(gè)包含所有操作的接口,會(huì)導(dǎo)致接口臃腫,增加了實(shí)現(xiàn)類的復(fù)雜性。

客戶端在實(shí)現(xiàn)時(shí)必須實(shí)現(xiàn)所有方法,即使它們中的某些方法在當(dāng)前場(chǎng)景中不需要使用。

解決方式:將大接口拆分成多個(gè)小接口,每個(gè)接口只包含相關(guān)的方法。這樣可以提高代碼的可維護(hù)性和靈活性,減少不必要的依賴。

06.單個(gè)API接口或函數(shù)

現(xiàn)在我們?cè)贀Q一種理解方式,把接口理解為單個(gè)接口或函數(shù)(以下為了方便講解,我都簡(jiǎn)稱為“函數(shù)”)。

那接口隔離原則就可以理解為:函數(shù)的設(shè)計(jì)要功能單一,不要將多個(gè)不同的功能邏輯在一個(gè)函數(shù)中實(shí)現(xiàn)。接下來(lái),我們還是通過(guò)一個(gè)例子來(lái)解釋一下。

public class Statistics {
  private Long max;
  private Long min;
  private Long average;
  private Long sum;
  private Long percentile99;
  private Long percentile999;
  //...省略constructor/getter/setter等方法...
}

public Statistics count(Collection<Long> dataSet) {
  Statistics statistics = new Statistics();
  //...省略計(jì)算邏輯...
  return statistics;
}

在上面的代碼中,count() 函數(shù)的功能不夠單一,包含很多不同的統(tǒng)計(jì)功能,比如,求最大值、最小值、平均值等等。

按照接口隔離原則,我們應(yīng)該把 count() 函數(shù)拆成幾個(gè)更小粒度的函數(shù),每個(gè)函數(shù)負(fù)責(zé)一個(gè)獨(dú)立的統(tǒng)計(jì)功能。拆分之后的代碼如下所示:

public Long max(Collection<Long> dataSet) { //... }
public Long min(Collection<Long> dataSet) { //... } 
public Long average(Colletion<Long> dataSet) { //... }
// ...省略其他統(tǒng)計(jì)函數(shù)...

不過(guò),你可能會(huì)說(shuō),在某種意義上講,count() 函數(shù)也不能算是職責(zé)不夠單一,畢竟它做的事情只跟統(tǒng)計(jì)相關(guān)。

在講單一職責(zé)原則的時(shí)候,也提到過(guò)類似的問(wèn)題。實(shí)際上,判定功能是否單一,除了很強(qiáng)的主觀性,還需要結(jié)合具體的場(chǎng)景。

如果在項(xiàng)目中,對(duì)每個(gè)統(tǒng)計(jì)需求,Statistics 定義的那幾個(gè)統(tǒng)計(jì)信息都有涉及,那 count() 函數(shù)的設(shè)計(jì)就是合理的。相反,如果每個(gè)統(tǒng)計(jì)需求只涉及 Statistics 羅列的統(tǒng)計(jì)信息中一部分,比如,有的只需要用到 max、min、average 這三類統(tǒng)計(jì)信息,有的只需要用到 average、sum。而 count()函數(shù)每次都會(huì)把所有的統(tǒng)計(jì)信息計(jì)算一遍,就會(huì)做很多無(wú)用功,勢(shì)必影響代碼的性能,特別是在需要統(tǒng)計(jì)的數(shù)據(jù)量很大的時(shí)候。所以,在這個(gè)應(yīng)用場(chǎng)景下,count()函數(shù)的設(shè)計(jì)就有點(diǎn)不合理了,我們應(yīng)該按照第二種設(shè)計(jì)思路,將其拆分成粒度更細(xì)的多個(gè)統(tǒng)計(jì)函數(shù)。

接口隔離原則跟單一職責(zé)原則有點(diǎn)類似,不過(guò)稍微還是有點(diǎn)區(qū)別。單一職責(zé)原則針對(duì)的是模塊、類、接口的設(shè)計(jì)。

接口隔離原則相對(duì)于單一職責(zé)原則,一方面它更側(cè)重于接口的設(shè)計(jì),另一方面它的思考的角度不同。它提供了一種判斷接口是否職責(zé)單一的標(biāo)準(zhǔn):通過(guò)調(diào)用者如何使用接口來(lái)間接地判定。如果調(diào)用者只使用部分接口或接口的部分功能,那接口的設(shè)計(jì)就不夠職責(zé)單一。

07.總結(jié)接口隔離原則

如何理解“接口隔離原則”? 理解“接口隔離原則”的重點(diǎn)是理解其中的“接口”二字。這里有三種不同的理解。

如果把“接口”理解為一組接口集合,可以是某個(gè)微服務(wù)的接口,也可以是某個(gè)類庫(kù)的接口等。如果部分接口只被部分調(diào)用者使用,我們就需要將這部分接口隔離出來(lái),單獨(dú)給這部分調(diào)用者使用,而不強(qiáng)迫其他調(diào)用者也依賴這部分不會(huì)被用到的接口。

如果把“接口”理解為單個(gè) API 接口或函數(shù),部分調(diào)用者只需要函數(shù)中的部分功能,那我們就需要把函數(shù)拆分成粒度更細(xì)的多個(gè)函數(shù),讓調(diào)用者只依賴它需要的那個(gè)細(xì)粒度函數(shù)。

如果把“接口”理解為 OOP 中的接口,也可以理解為面向?qū)ο缶幊陶Z(yǔ)言中的接口語(yǔ)法。那接口的設(shè)計(jì)要盡量單一,不要讓接口的實(shí)現(xiàn)類和調(diào)用者,依賴不需要的接口函數(shù)。

接口隔離原則與單一職責(zé)原則的比較,他們各自的特點(diǎn):

  1. 單一職責(zé)原則針對(duì)的是模塊、類、接口的設(shè)計(jì)。接口隔離原則相對(duì)于單一職責(zé)原則,一方面更側(cè)重于接口的設(shè)計(jì),另一方面它的思考角度也是不同的。
  2. 接口隔離原則提供了一種判斷接口的職責(zé)是否單一的標(biāo)準(zhǔn):通過(guò)調(diào)用者如何使用接口來(lái)間接地判定。如果調(diào)用者只使用部分接口或接口的部分功能,那接口的設(shè)計(jì)就不夠職責(zé)單一。

接口隔離原則與單一職責(zé)原則的區(qū)別

  1. 關(guān)注點(diǎn)不同:?jiǎn)我宦氊?zé)原則關(guān)注的是類或模塊的職責(zé),它強(qiáng)調(diào)將不同的職責(zé)分離開(kāi)來(lái),使得類或模塊的設(shè)計(jì)更加清晰和可維護(hù)。而接口隔離原則關(guān)注的是接口的設(shè)計(jì),即將大而全的接口拆分為小而精簡(jiǎn)的接口,以避免類實(shí)現(xiàn)不需要的方法,減少接口的冗余和龐大。
  2. 范圍不同:?jiǎn)我宦氊?zé)原則是針對(duì)類或模塊級(jí)別的設(shè)計(jì)原則,它要求一個(gè)類或模塊只負(fù)責(zé)一項(xiàng)職責(zé)。而接口隔離原則是針對(duì)接口級(jí)別的設(shè)計(jì)原則,它要求將接口細(xì)化為只包含相關(guān)方法的小接口,以滿足不同類或模塊的特定需求。
  3. 目的不同:?jiǎn)我宦氊?zé)原則的目的是提高類或模塊的內(nèi)聚性,使其職責(zé)清晰、單一,易于理解和維護(hù)。而接口隔離原則的目的是提高系統(tǒng)的靈活性、可維護(hù)性和可擴(kuò)展性,通過(guò)拆分接口,避免類實(shí)現(xiàn)不需要的方法,減少接口的冗余和龐大。

08.思考一道課后題

java.util.concurrent 并發(fā)包提供了 AtomicInteger 這樣一個(gè)原子類,其中有一個(gè)函數(shù) getAndIncrement()是這樣定義的:給整數(shù)增加一,并且返回未増之前的值。

我的問(wèn)題是,這個(gè)函數(shù)的設(shè)計(jì)是否符合單一職責(zé)原則和接口隔離原則?為什么?

/**
 * Atomically increments by one the current value.
 * @return the previous value
 */
public final int getAndIncrement() {//...}

09.接口隔離原則總結(jié)

  1. 接口隔離問(wèn)題思考:什么叫作接口隔離法則,它和面向?qū)ο笾械慕涌谟泻螀^(qū)別?在哪些場(chǎng)景需要注意接口隔離原則?
  2. 如何理解接口隔離原則:簡(jiǎn)單來(lái)說(shuō),這個(gè)原則鼓勵(lì)將龐大而臃腫的接口拆分為更小、更具體的接口,以便客戶端只需依賴它們所需的接口。
  3. 接口隔離原則的核心思想:1.接口應(yīng)該精簡(jiǎn);2.接口應(yīng)該獨(dú)立;3.接口應(yīng)該可以可拓展。
  4. 如何理解這個(gè)原則中的接口:可以把它看作一組抽象的約定,也可以指系統(tǒng)與系統(tǒng)之間的API接口,還可以特指面向?qū)ο缶幊陶Z(yǔ)言中的接口等。
  5. 接口隔離原則的背景:常常使用大而全的接口來(lái)定義類之間的交互方式,會(huì)導(dǎo)致接口臃腫,接口新增方法強(qiáng)迫子類都實(shí)現(xiàn),脆弱性。為了解決這些問(wèn)題才產(chǎn)生這個(gè)原則。
  6. 實(shí)現(xiàn)接口隔離原則的方式:1.拆分大接口;2.定義細(xì)化接口;3.接口繼承;4.接口組合;5.依賴注入
  7. 接口隔離原則的案例教學(xué):看博客中的代碼案例
  8. 接口隔離原則的優(yōu)點(diǎn):接口隔離原則的優(yōu)點(diǎn)包括精簡(jiǎn)接口、避免強(qiáng)迫實(shí)現(xiàn)、提高靈活性、降低耦合性和提高可復(fù)用性。
  9. 接口隔離原則的缺點(diǎn):可能導(dǎo)致接口數(shù)量增多,接口增多可能帶來(lái)代碼復(fù)雜,可能引入間接性的依賴關(guān)系。
  10. 接口隔離原則與單一職責(zé)原則的區(qū)別:主要區(qū)別有,他們關(guān)注點(diǎn)不同,使用范圍不同,還有目的不同。

10.更多內(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,異常,線程和并發(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ǔ)言入門級(jí)別系統(tǒng)全面的學(xué)習(xí)教程,學(xué)習(xí)三到四個(gè)綜合案例 C編程
C++編程 C++語(yǔ)言入門級(jí)別系統(tǒng)全面的教學(xué)教程,并發(fā)編程,核心原理 C++編程
算法實(shí)踐 專欄,數(shù)組,鏈表,棧,隊(duì)列,樹(shù),哈希,遞歸,查找,排序等 Leetcode
Android 基礎(chǔ)入門,開(kāi)源庫(kù)解讀,性能優(yōu)化,F(xiàn)ramework,方案設(shè)計(jì) Android
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容