Dependency inversion principle

依賴倒置原則(Dependency inversion principle)有兩個主要部分:

  • 高層模組不應該依賴於低層模組。兩者都應該依賴於抽象。
  • 抽象不應該依賴於具體實現。具體實現應該依賴於抽象。

在軟體設計中,如果一個類(class)使用了另一個類稱為依賴(dependencycoupling),每增加一點依賴,就會增加一些風險,因為當某個類A知道了另一個類B的內容太多的話(稱為高度耦合high degree of coupling),當B更改的話,那麼A也會需要大量的修改,這樣很容易產生錯誤。

在設計中,有些類是high-level的,而有些類是low-level的,high-level的類會依靠low-level的類去完成某些工作。我們在設計時,要考慮依賴倒置原則,減少一些耦合度。

如果要建立一個遊戲,其中角色可以探索房間,並使用開關(switch)將門(dor)打開,在實作時,你可能會想到要建立一個Switch類與一個Door類,其中

  • Switch屬於high-level的類,它負責判斷角色是否移動到對應的位置,並是否觸發對應的行為
  • Door屬於low-level的類,它負責開關門的實際操作邏輯

如果沒有使用依賴倒置原則的話,可能會如下實作

  • Switch依賴於Door,當條件觸發時,呼叫door去開門
    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
    public class Switch
    {
    public Door door;
    public bool isActivated;

    public void Toggle()
    {
    if (isActivated)
    {
    isActivated = false;
    door.Close();
    }
    else
    {
    isActivated = true;
    door.Open();
    }
    }
    }

    public class Door
    {
    public void Open()
    {
    Debug.Log("The door is open.");
    }

    public void Close()
    {
    Debug.Log("The door is closed.");
    }
    }

這樣的實作沒有問題,但是如果開關(Switch)不只是開門,還可能會觸發一些陷阱的話,就不得不去修改Switch類,這樣違反了開閉原則(Open-closed principle)

你可以將開關這個動作抽象=>ISwitchable。

1
2
3
4
5
6
public interface ISwitchable
{
bool IsActive { get; }
void Activate();
void Deactivate();
}

讓Door去實作這個抽象ISwitchable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Door : MonoBehaviour, ISwitchable
{
private bool isActive;
public bool IsActive => isActive;

public void Activate()
{
isActive = true;
Debug.Log("The door is open.");
}

public void Deactivate()
{
isActive = false;
Debug.Log("The door is closed.");
}
}

而Switch則依賴這個抽象ISwitchable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Switch : MonoBehaviour
{
public ISwitchable client;

public void Toggle()
{
if (client.IsActive)
{
client.Deactivate();
}
else
{
client.Activate();
}
}
}

透過這種方式將上層對底層的依賴剝離至抽象,讓上層可以不需更改程式碼,只需傳入不同的ISwitch實作便可以做到開關不同的物件。

評論