設(shè)計(jì)模式六大原則(四)----接口隔離原則

一. 接口隔離原則的定義

Clients should not be forced to depend upon interfaces that they don't use.

客戶(hù)端只依賴(lài)于它所需要的接口;它需要什么接口就提供什么接口,把不需要的接口剔除掉。

The dependency of one class to another one should depend on the smallest possible interface.

類(lèi)間的依賴(lài)關(guān)系應(yīng)建立在最小的接口上。

也就是說(shuō): 接口盡量細(xì)化,接口中的方法盡量少

二. 接口隔離原則和單一職責(zé)原則

從功能上來(lái)看,接口隔離原則和單一職責(zé)原則都是為了提高類(lèi)的內(nèi)聚, 降低類(lèi)之間的耦合, 體現(xiàn)了封裝的思想。但二者還是有區(qū)別的。

(1)從原則約束來(lái)看: 接口隔離原則更關(guān)注的是接口依賴(lài)程度的隔離;而單一職責(zé)原則更加注重的是接口職責(zé)的劃分。

(2)從接口的細(xì)化程度來(lái)看: 單一職責(zé)原則對(duì)接口的劃分更加精細(xì),而接口隔離原則注重的是相同功能的接口的隔離。接口隔離里面的最小接口有時(shí)可以是多個(gè)單一職責(zé)的公共接口。

(3)單一職責(zé)原則更加偏向?qū)I(yè)務(wù)的約束: 接口隔離原則更加偏向設(shè)計(jì)架構(gòu)的約束。這個(gè)應(yīng)該好理解,職責(zé)是根據(jù)業(yè)務(wù)功能來(lái)劃分的,所以單一原則更加偏向業(yè)務(wù);而接口隔離更多是為了“高內(nèi)聚”,偏向架構(gòu)的設(shè)計(jì)。

三. 接口隔離原則的優(yōu)點(diǎn)

接口隔離原則是為了約束接口、降低類(lèi)對(duì)接口的依賴(lài)性,遵循接口隔離原則有以下 5 個(gè)優(yōu)點(diǎn)。

  1. 將臃腫龐大的接口分解為多個(gè)粒度小的接口,可以預(yù)防外來(lái)變更的擴(kuò)散,提高系統(tǒng)的靈活性和可維護(hù)性。
  2. 接口隔離提高了系統(tǒng)的內(nèi)聚性,減少了對(duì)外交互,降低了系統(tǒng)的耦合性。
  3. 如果接口的粒度大小定義合理,能夠保證系統(tǒng)的穩(wěn)定性;然而,如果定義過(guò)小,則會(huì)造成接口數(shù)量過(guò)多,使設(shè)計(jì)復(fù)雜化;如果定義太大,靈活性降低,無(wú)法提供定制服務(wù),給整體項(xiàng)目帶來(lái)無(wú)法預(yù)料的風(fēng)險(xiǎn)。
  4. 使用多個(gè)專(zhuān)門(mén)的接口能夠體現(xiàn)對(duì)象的層次,因?yàn)榭梢酝ㄟ^(guò)接口的繼承,實(shí)現(xiàn)對(duì)總接口的定義。
  5. 能減少項(xiàng)目工程中的代碼冗余。過(guò)大的大接口里面通常放置許多不用的方法,當(dāng)實(shí)現(xiàn)這個(gè)接口的時(shí)候,被迫設(shè)計(jì)冗余的代碼。

四. 接口隔離原則的實(shí)現(xiàn)方法

在具體應(yīng)用接口隔離原則時(shí),應(yīng)該根據(jù)以下幾個(gè)規(guī)則來(lái)衡量。
1)接口要盡量小
不能出現(xiàn)Fat Interface;但是要有限度,首先不能違反單一職責(zé)原則(不能一個(gè)接口對(duì)應(yīng)半個(gè)職責(zé))。

2)接口要高內(nèi)聚
在接口中盡量少公布public方法。
接口是對(duì)外的承諾,承諾越少對(duì)系統(tǒng)的開(kāi)發(fā)越有利。

3)定制服務(wù)
只提供訪(fǎng)問(wèn)者需要的方法。例如,為管理員提供IComplexSearcher接口,為公網(wǎng)提供ISimpleSearcher接口。

4)接口的設(shè)計(jì)是有限度的
了解環(huán)境,拒絕盲從。每個(gè)項(xiàng)目或產(chǎn)品都有選定的環(huán)境因素,環(huán)境不同,接口拆分的標(biāo)準(zhǔn)就不同, 需要深入了解業(yè)務(wù)邏輯。

五. 接口隔離原則的建議

  1. 一個(gè)接口只服務(wù)于一個(gè)子模塊或業(yè)務(wù)邏輯;
  2. 通過(guò)業(yè)務(wù)邏輯壓縮接口中的public方法;
  3. 已被污染了的接口,盡量去修改;若變更的風(fēng)險(xiǎn)較大,則采用適配器模式轉(zhuǎn)化處理;
  4. 拒絕盲從

五. 案例分析

下面以學(xué)生成績(jī)管理為例來(lái)說(shuō)明接口隔離原則:

分析:學(xué)生成績(jī)管理程序一般包含查詢(xún)成績(jī)、新增成績(jī)、刪除成績(jī)、修改成績(jī)、計(jì)算總分、計(jì)算平均分、打印成績(jī)信息等功能,通常我們會(huì)怎么做呢?

一: 最初的設(shè)計(jì)

通常我們?cè)O(shè)計(jì)接口的方式如下:

public interface IStudentScore {
    // 查詢(xún)成績(jī)
    public void queryScore();

    // 修改成績(jī)
    public void updateScore();

    // 添加成績(jī)
    public void saveScore();

    // 刪除成績(jī)
    public void delete();

    // 計(jì)算總分
    public double sum();

    // 計(jì)算平均分
    public double avg();

    // 打印成績(jī)單
    public void printScore();

}

我們會(huì)吧所有的功能都放在一個(gè)接口里面. 這會(huì)產(chǎn)生什么樣的問(wèn)題呢?
首先, 接口的方法很多, 不利于擴(kuò)展. 比如: 學(xué)生只有查看成績(jī),打印成績(jī)單的權(quán)限, 沒(méi)有增刪改的權(quán)限; 老師擁有所有的權(quán)限.
查詢(xún)成績(jī)單:

package com.lxl.www.designPatterns.sixPrinciple.interfaceSegregationPrinciple.score;

public class QueryScore implements IStudentScore{
    @Override
    public void queryScore() {
        // 查詢(xún)成績(jī)
    }

    @Override
    public void updateScore() {
         // 沒(méi)有權(quán)限
    }

    @Override
    public void saveScore() {
        // 沒(méi)有權(quán)限
    }

    @Override
    public void delete() {
        // 沒(méi)有權(quán)限
    }

    @Override
    public double sum() {
        // 沒(méi)有權(quán)限
        return 0;
    }

    @Override
    public double avg() {
        // 沒(méi)有權(quán)限
        return 0;
    }

    @Override
    public void printScore() {
        //打印成績(jī)單
    }
}

操作成績(jī)單

package com.lxl.www.designPatterns.sixPrinciple.interfaceSegregationPrinciple.score;

public class Operate implements IStudentScore{
    @Override
    public void queryScore() {
        
    }

    @Override
    public void updateScore() {

    }

    @Override
    public void saveScore() {

    }

    @Override
    public void delete() {

    }

    @Override
    public double sum() {
        return 0;
    }

    @Override
    public double avg() {
        return 0;
    }

    @Override
    public void printScore() {

    }
}

可以看出問(wèn)題. 查詢(xún)成績(jī)單, 我們只會(huì)用到兩個(gè)方法, 可是因?yàn)閷?shí)現(xiàn)了接口, 不得不重寫(xiě)所有的方法.
如果這時(shí)候增加需求--發(fā)送給家長(zhǎng), 只有老師才有這個(gè)權(quán)限, 學(xué)生沒(méi)有這個(gè)權(quán)限. 可是, 在接口中增加一個(gè)抽象方法以后, 所有的實(shí)現(xiàn)類(lèi)都要重寫(xiě)這個(gè)方法. 這就違背了開(kāi)閉原則.

2. 使用接口隔離原則的設(shè)計(jì)

采用接口隔離原則設(shè)計(jì)的接口, UML圖如下:

public interface IQueryScore {
    // 查詢(xún)成績(jī)
    public void queryScore();

    // 打印成績(jī)單
    public void printScore();
}

public interface IOperateScore {

    // 修改成績(jī)
    public void updateScore();

    // 添加成績(jī)
    public void saveScore();

    // 刪除成績(jī)
    public void delete();

    // 計(jì)算總分
    public double sum();

    // 計(jì)算平均分
    public double avg();

}


public class StudentOperate implements IQueryScore{
    @Override
    public void queryScore() {
        // 查詢(xún)成績(jī)
    }

    @Override
    public void printScore() {
        //打印成績(jī)單
    }
}


public class TeacherOperate implements IQueryScore, IOperateScore{
    @Override
    public void queryScore() {

    }

    @Override
    public void updateScore() {

    }

    @Override
    public void saveScore() {

    }

    @Override
    public void delete() {

    }

    @Override
    public double sum() {
        return 0;
    }

    @Override
    public double avg() {
        return 0;
    }

    @Override
    public void printScore() {

    }
}

我們將原來(lái)的一個(gè)接口進(jìn)行了接口拆分. 分為查詢(xún)接口和操作接口. 這樣學(xué)生端就不需要重寫(xiě)和他不相關(guān)的接口了.

如果將這些功能全部放到一個(gè)接口中顯然不太合理,正確的做法是將它們分別放在輸入模塊、統(tǒng)計(jì)模塊和打印模塊等 3 個(gè)模塊中,其類(lèi)圖如圖 1 所示

?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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