介面隔離原則

Make fine grained interfaces that are client specific.
Robert C. Martin

介面隔離原則,全名 Interface segregation principle,簡稱 ISP

定義

用戶端程式碼不應該依賴它用不到的介面,依賴的介面都是有其必要性。
把不同功能的從介面中分離出來。

其實我只是要打個果汁
圖片來源

秘訣

  • 把 Interface 當成「可以做什麼」, 而不是「是一個什麼」
  • 減少讓每個 Interface 可以做的事
  • 如果發現有空實作時,就表示 Interface 可以再細化

範例

來看看範例。
身為一個沒朋友的程序猿,最好的朋友就是家裡的毛小孩。
那就先來寫個寵物類別,再來條狗和一隻貓,最好還要能跟我玩飛盤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
interface Pet
{
public function eat(): void;
public function sleep(): void;
public function catchFrisbee(): void;
}

class Dog implements Pet
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}

public function catchFrisbee(): void
{
...
}
}

class Cat implements Pet
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}

public function catchFrisbee(): void
{
//cats do not catch frisbees
throw new Exception('大膽刁民,竟敢造次!(抓)');
}
}

醒醒吧!貓皇是不會和你玩飛盤的…
但是因為是實作了 Pet 介面,硬著頭皮還是要實作 catchFrisbee,
結果你家主子不理你,還違反里氏替換原則了…
所以,還是不要這樣搞了吧!再來改一改…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
interface Pet
{
public function eat(): void;
public function sleep(): void;
}

interface Play
{
public function catchFrisbee(): void;
}

class Dog implements Pet
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}
}

class TrainedDog implements Pet, Play
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}

public function catchFrisbee(): void
{
...
}
}

class Cat implements Pet
{
public function eat(): void
{
...
}

public function sleep(): void
{
...
}
}

這次我們把接飛盤這個動作從 Pet 類別抽出來,單獨定義成一個 Play 介面,以後不要再強迫你的主子陪你玩飛盤了。然後,再定義一個 TrainedDog 類別,只有這個類別實作 Play 介面,畢竟狗狗要能接飛盤也是要經過訓練的嘛!

Reference

PHP 也有 Day #19 - PHP 返樸歸真系列之從實例學設計模式 by 大澤木小鐵 (Jace Ju)​
物件導向設計原則 SOLID
SOLID:五則皆變
The Principles of OOD