> For the complete documentation index, see [llms.txt](https://brianwu.gitbook.io/brian/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://brianwu.gitbook.io/brian/test/guan-nian.md).

# IoC & DI

## Dependency Injection 依賴注入 (DI)

參考資料:Dependency Injection 筆記(<https://www.huanlintalk.com/post/2011-10-14-dependency-injection-1/>)

## 如何向五歲的孩童解釋 DI？

該帖的問題是：「如何向五歲的孩童解釋 DI？」在眾多回答中，有位名叫 John Munch 的仁兄假設提問者就是那五歲的孩童而給了如下答案：

```
當你自己去開冰箱拿東西時，很可能會闖禍。
你可能忘了關冰箱門、可能會拿了爸媽不想讓你碰的東西，
甚至冰箱裡根本沒有你想要找的食物，又或者它們早已過了保存期限。
你應該把自己需要的東西說出來就好，
例如：「我想要一些可以搭配午餐的飲料。」
然後，當你坐下用餐時，我們會準備好這些東西。
```

## 物件導向設計原則

SRP（Single Responsibility Principle）：單一責任原則。一個類別應該只有一個責任。

OCP（Open/Closed Principle）：開放／封閉原則。對開放擴充，對修改封閉。

LSP（Liskov Substitution Principle）：里氏替換原則。物件應該要可以被它的子類別的物件替換，且完全不影響程式的既有行為。

ISP（Interface Segregation Principle）：介面隔離原則。多個規格明確的小介面要比一個包山包海的大型介面好。

DIP（Dependency Inversion Principle）：相依反轉原則。依賴抽象型別，而不是具象型別。

### Inversion of Control 控制反轉（IoC）

一般的做法: 上層呼叫某個高階模組的方法，該方法 new 一個低階模組的物件出來用。 這樣這個方法就會綁定這個物件。

但如果有多種類似的物件呢?

利用 介面(interface) 來解耦，將這些類別抽象化(共通的部分抽出來，做成介面)，使這些共通性成為規格，而那些物件再實作這個介面、實作這些規格。

DIP: 高階模組不應依賴低階模組，它們應該去相依抽象層。 抽象層不應依賴實作細節，而是實作細節去依賴抽象層。

之後在使用時，由於不能new 出介面，那麼物件從何而來? 答案是由上方 new 出想要的物件，傳到該方法去讓底部使用。

原本在底部 new 物件出來，改成了從上層傳物件到底部去用，所以控制反轉了。

### Dependency Injection 依賴注入 (DI)

一種設計模式和原則，用來寬鬆耦合。

以上方舉例來說，由呼叫端將相依物件透過建構函式，注入至另一個物件的作法，是DI的一種常見寫法，叫作「建構式注入」（Constructor Injection）。

DI的三種注入方式: 1.建構式注入（Constructor Injection） 2.屬性注入（Property Injection） 3.方法注入（Method Injection）

#### Note: 每當需要注入相依物件時，一般建議優先考慮「建構式注入」，因為其用法對呼叫端來說相當明確、直覺——建立物件時就要一併傳入所有相依物件，所以呼叫端透過建構函式便可得知某物件相依於哪些第三方元件。

### 介面導向設計

原本相依的物件，現在都透過相依於介面。

當要測試時，再從外部將實作介面的測試物件傳入，去驗證其商業邏輯。

## 這個把初始化動作，由原本目標物件內，轉移到目標物件之外，稱作「控制反轉」，也就是 IoC。

## 這個把依賴的物件，透過目標物件公開建構式，交給外部來決定，稱作「依賴注入」，也就是 DI。

## 而 IoC 跟 DI，其實就是同一件事：讓外部決定目標物件的相依物件。

而這個介面，也可以是目標物件的擴充點，或是接縫。
