1. 程式人生 > >第2章 面向物件的設計原則(SOLID):4_介面隔離原則(ISP)

第2章 面向物件的設計原則(SOLID):4_介面隔離原則(ISP)

4. 介面隔離原則(Interface Segregation Principle,ISP)

4.1 定義

(1)使用多個專門的介面,而不使用單一的總介面,即客戶端不應該依賴那些它不需要的介面。類間的依賴關係應該建立在最小介面

(2)介面儘量細化,同時介面中的方法儘量少。每個介面中只包含一個客戶端(如子模組或業務邏輯類)所需的方法即可,這種機制也稱為“定製服務”,即為不同的客戶端提供寬窄不同的介面。

(3)為介面添加了不必要的方法,這叫介面汙染。這對於實現類來講,就是一種汙染,他們會被迫去實現這些不必要的功能方法。並且當介面發生改變時,他們也不得不跟著改變。

4.2 介面隔離原則和單一職責原則的區別

(1)單一職責注意的是職責,是從業務邏輯上劃分的。而介面隔離要求介面要儘量少。如有一個向外提供查詢服務的,但提供的介面很多,有普通許可權和高階許可權的查詢方法。這按單一職責原則是允許的,只提供單一職責,即查詢。但按照介面隔離原則是不允許的。應按許可權提供專門的介面給不同許可權的客戶端。

(2)介面隔離原則講的就是同一個角色提供寬、窄不同的介面,以對付不同的客戶端。

4.3 使用介面原則的注意事項

(1)介面儘量小,但要有限度。對介面進行細行可以提高設計靈活性,但如果過小,則會造成介面數量過多,使設計模式複雜化

(2)為依賴介面的類定製服務,只暴露給呼叫它的類所需要的方法,不需要的則隱藏起來。

(3)提高內聚,減少對外互動。使介面用最小的方法去完成最多的事情。具體要求就是,在介面中儘量少提供public方法,介面是對外的承諾,承諾地越少對系統開發越有利,變更的風險也就越少,同時也有利於降低成本。

【程式設計實驗】介面隔離的例子

  ①Manager類代表管理工人的管理者。

  ②有兩類工人,普通和高效的,他們都需要吃飯和工作

  ③如果這裡來了一個機器人(Robot),如果保持現有設計,讓Robot去實現IWork介面,就會被迫去實現吃飯的功能,這種情況下IWorker被認為是一個被汙染了的介面。需重構,細化出3個介面,一個IWorkable(工作)、IFeedable(吃飯)和IRechargeable(充電)。

  ④工人類去實現IWorkable和IFeedable介面。Robot只實現IWorkable介面和 IRechargeable介面。

//當加入Robot類後,一個被汙染的介面設計

//重構後符合介面隔離原則的設計

//面向物件設計原則:ISP介面隔離原則
//細化介面

#include <stdio.h>

//IWorkable介面:工作
class IWorkable
{
public:
    virtual void work() = 0;
};

//IFeedable介面:吃飯
class IFeedable
{
public:
    virtual void eat() = 0;
};

//IRechargeable介面:充電
class IRechargeable
{
public:
    virtual void recharge() = 0;
};

//CommonWorker類:普通工人
class CommonWorker : public IWorkable, public IFeedable
{
public:
    void eat(){printf("Common Worker eat()...\n");}
    void work(){printf("Common Worker work()...\n");}   
};

//EfficentWork類:高效工人
class EfficentWork : public IWorkable, public IFeedable
{
public:
    void eat(){printf("Efficent Worker eat()....\n");}
    void work(){printf("Efficent Worker work()...\n");}   
};

//Robot類:機器人
class Robot : public IWorkable, public IRechargeable
{
public:
    void recharge(){printf("Robot recharge()...\n");}
    void work(){printf("Robot work()...\n");}   
};

//Manager類:管理者
class Manager
{
private:
    IWorkable* worker;
public:
    void setWorker(IWorkable* w){worker = w;}
    void manage(){worker->work();}
};
int main()
{
    
    CommonWorker cmw;
    EfficentWork efw;
    Robot rb;
    
    Manager mn;
    
    //管理員讓普通工人工作
    mn.setWorker(&cmw);
    mn.manage();
    
    //管理員讓高效工人工作
    mn.setWorker(&efw);
    mn.manage();
    
    //管理員讓機器人工作
    mn.setWorker(&rb);
    mn.manage();
    
    return 0;
}

4.4 最佳實踐

(1)一個介面只服務於一個子模組或業務邏輯

(2)通過業務邏輯壓縮介面中的public方法,以提高內聚

(3)己經被汙染了的介面,儘量去修改,若變更的風險較大,則採用介面卡模式進行轉化。