- High-level modules should not depend on low-level modules. Both should depend on abstractions.
高層模組不應該依賴低層模組,兩者皆應依賴於抽象。
- 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 的主要目的是讓我們在設計類別時能夠盡量相依在抽象上面,之後如果需要加上其他實作的時候就會變得比較有擴充性,改動的地方也就不會這麼多了。
參考資料