S.O.L.I.D 物件導向設計原則 - 相依反轉原則 Dependency Inversion Principle

Posted by Ryan Tseng on 2018-04-08
  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.

高層模組不應該依賴低層模組,兩者皆應依賴於抽象。

  1. Abstractions should not depend on details. Details should depend on abstractions.

抽象不應該依賴細節,細節應該依賴於抽象。

實例

很快的我們用一個簡單的例子來說明一下 DIP

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
public class PaymentService
{
private BankAccount _bankAccount;
public PaymentService(BankAccount bankAccount)
{
_bankAccount = bankAccount;
}

public void Checkout(decimal fee)
{
_bankAccount.RemoveBalance(fee);
}

public void Refund(decimal fee)
{
_bankAccount.AddBalance(fee);
}
}

public class BankAccount
{
public decimal Balance { get; set; }

public void AddBalance(decimal value)
{
Balance += value;
}

public void RemoveBalance(decimal value)
{
Balance -= value;
}
}

假設今天 BankAccount 類別,因為支付的服務需要換接別的銀行帳號,那這時候是不是就得把 BankAccount 的實作也一起換掉,這樣的話豈不是會需要改掉系統裡面任何用到 BankAccount 的地方了嗎?而且這樣也會違反了 OCP 原則 (對修改封閉、對擴充開放)。

為了解決這樣的問題,所以我們在設計類別的時候,盡量能夠將相依的部分改為相依到抽象,而不是實作,因此上面的範例程式我們可以這樣修改。

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
public class PaymentService
{

private IBankAccount _bankAccount;
public PaymentService(IBankAccount bankAccount)
{
_bankAccount = bankAccount;
}

public void Checkout(decimal fee)
{
this._bankAccount.RemoveBalance(fee);
}

public void Refund(decimal fee)
{
this._bankAccount.AddBalance(fee);
}
}

public interface IBankAccount
{
void AddBalance(decimal value);
void RemoveBalance(decimal value);
}

public class BankAccount : IBankAccount
{
public decimal Balance { get; set; }

public void AddBalance(decimal value)
{
Balance += value;
}

public void RemoveBalance(decimal value)
{
Balance -= value;
}
}

BankAccount 在我們的修改底下相依到了 IBankAccount 介面,那麼以後如果要改成別的銀行的 BankAccount,只要再加上其他類別的實作即可,

小結

經由這樣的演示,我們可以知道 DIP 的主要目的是讓我們在設計類別時能夠盡量相依在抽象上面,之後如果需要加上其他實作的時候就會變得比較有擴充性,改動的地方也就不會這麼多了。

參考資料