第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)己經被汙染了的介面,儘量去修改,若變更的風險較大,則採用介面卡模式進行轉化。