控制反转

**控制反轉**（英語：Inversion of Control，縮寫為 **IoC**），是物件導向程式設計中的一種設計原則，可以用來減低電腦代碼之間的耦合度。其中最常見的方式叫做**依賴注入**（Dependency Injection，簡稱 **DI**），還有一種方式叫「依賴尋找」（Dependency Lookup）。

## 起源

早在 2004 年，Martin Fowler 就提出了「哪些方面的控制被反轉了？」這個問題。他總結出是依賴物件的獲得被反轉了，因為大多數應用程式都是由兩個或是更多的類通過彼此的合作來實現業務邏輯，這使得每個物件都需要取得與其合作的物件（也就是它所依賴的物件）的參照。如果這個取得過程要靠自身實現，那麼這將導致代碼高度耦合並且難以維護和除錯。

## 技術描述

Class A 中用到了 Class B 的物件 b，一般情況下，需要在 A 的代碼中顯式地用 new 建立 B 的物件。

採用依賴注入技術之後，A 的代碼只需要定義一個 private 的 B 物件，不需要直接 new 來獲得這個物件，而是通過相關的容器控制程式來將 B 物件在外部 new 出來並注入到 A 類里的參照中。而具體取得的方法、物件被取得時的狀態由設定檔（如 XML）來指定。

IoC 也可以理解為把流程的控制從應用程式轉移到框架之中。以前，應用程式掌握整個處理流程；現在，控制權轉移到了框架，框架利用一個引擎驅動整個流程的執行，框架會以相應的形式提供一系列的擴充點，應用程式則通過定義擴充的方式實現對流程某個環節的客製化，「框架 Call 應用」。基於 MVC 的 web 應用程式就是如此。

## 實現方法

實現控制反轉主要有兩種方式：依賴注入和依賴尋找。兩者的區別在於，前者是被動的接收物件，在類 A 的實例建立過程中即建立了依賴的 B 物件，通過類型或名稱來判斷將不同的物件注入到不同的屬性中，而後者是主動索取相應類型的物件，獲得依賴物件的時間也可以在代碼中自由控制。

### 依賴注入

依賴注入有如下實現方式：

* 基於介面。實現特定介面以供外部容器注入所依值型別的物件。
* 基於 set 方法。實現特定屬性的 public set 方法，來讓外部容器呼叫傳入所依值型別的物件。
* 基於建構函式。實現特定參數的建構函式，在新建物件時傳入所依值型別的物件。
* 基於註解。基於 Java 的註解功能，在私有變數前加「@Autowired」等註解，不需要顯式的定義以上三種代碼，便可以讓外部容器傳入對應的物件。該方案相當於定義了 public 的 set 方法，但是因為沒有真正的 set 方法，從而不會為了實現依賴注入導致暴露了不該暴露的介面（因為 set 方法只想讓容器存取來注入而並不希望其他依賴此類的物件存取）。

### 依賴尋找

依賴尋找更加主動，在需要的時候通過呼叫框架提供的方法來取得物件，取得時需要提供相關的設定檔路徑、key 等資訊來確定取得物件的狀態

## 控制反轉應用實例

### C++

* PocoCapsule IoC and DSM framework （頁面存檔備份，存於網際網路檔案館） LGPL 開源的，支援完全非侵入 C++ 的控制反轉（IoC）及領域特定建模（DSM）容器
* hypodermic （頁面存檔備份，存於網際網路檔案館） MIT 開源協定，Hypodermic 是一個基於 C++11 開發的控制反轉（IoC）容器，它為你的 C++ 物件協同運作提供依賴注入。Hypodermic 靈感來自著名的.NET IoC 專案 Autofac。

### Java

使用 Java 語言寫成的程式在控制反轉容器（_Inversion of Control Container_）裡應用了控制反轉 (Martin 2004)。軟體需要一個來自容器的物件，而容器自行建構物件和它的附屬物。ATG 的 Dynamo 應用程式伺服器是第一個利用這途徑的環境之一，近來關於這些容器的例子包含了 HiveMind、PicoContainer （頁面存檔備份，存於網際網路檔案館）、Spring Framework（注意 Spring 是一個完整的企業平台，而非 IOC 容器）、Apache Excalibur、Seasar 和 DPML Metro.

### .NET

* Ninject （頁面存檔備份，存於網際網路檔案館）
* Spring.NET （頁面存檔備份，存於網際網路檔案館）
* ObjectBuilder （頁面存檔備份，存於網際網路檔案館）
* Structuremap （頁面存檔備份，存於網際網路檔案館）
* CastleProject
* Seasar （頁面存檔備份，存於網際網路檔案館）
* Autofac （頁面存檔備份，存於網際網路檔案館）
* Unity （頁面存檔備份，存於網際網路檔案館）
* MET

### PHP

* Phalcon PHP 框架中應用了依賴注入：DI in Phalcon （頁面存檔備份，存於網際網路檔案館）
* Laravel PHP 框架核心的服務容器支援依賴注入：Service Container

## 參考文件

1. **^** Robert Cecil Martin. Agile Software Development: Principles, Patterns and Practices. Pearson Education. 2002. ISBN 978-0-13-597444-5.
2. **^** Robert Cecil Martin. The Dependency Inversion Principle (PDF). [2005-11-15]. （原始內容 (PDF) 存檔於 2004-12-21）.
3. **^** Martin Fowler. Inversion of Control Containers and the Dependency Injection Pattern. 2004 [2005-11-15]. （原始內容存檔於 2020-05-30）.
4. **^** Sony Mathew. Examining the Validity of Inversion of Control. 2005 [2005-11-16]. （原始內容存檔於 2005-11-29）.
5. **^** Ke Jin. Domain Specific Modeling (DSM) in IoC frameworks. 2007 [2007-11-13]. （原始內容存檔於 2007-11-15）.

## 外部連結

* Another description of IOC
* A list of "Open Source Inversion of Control Containers （頁面存檔備份，存於網際網路檔案館）"
* A simple demo of Inversion of Control (using Spring framework)
* Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler （頁面存檔備份，存於網際網路檔案館）
* Needle, a dependency injection (inversion of control) container for Ruby （頁面存檔備份，存於網際網路檔案館）
* Drip IoC （頁面存檔備份，存於網際網路檔案館）, a dependency injection (inversion of control) container for PHP4 （頁面存檔備份，存於網際網路檔案館）
* PyContainer （頁面存檔備份，存於網際網路檔案館）, a dependency injection (inversion of control) container for Python （頁面存檔備份，存於網際網路檔案館）
* Qt Ioc Container （頁面存檔備份，存於網際網路檔案館）, a dependency injection (inversion of control) container for C++ （頁面存檔備份，存於網際網路檔案館）
* Introducing Castle, explains the problems that Inversion of control containers try to solve (using Castle Project)
* PocoCapsule （頁面存檔備份，存於網際網路檔案館） An open source IoC and DSM framework for C++
* Domain Specific Modeling (DSM) in IoC frameworks （頁面存檔備份，存於網際網路檔案館）