IoC/DI :
IoC — Inversion of Control,控制反轉
DI — Dependency Injection,依賴注入
IoC,是一種 設計原則:
藉由 『分離組件 (Components) 的設置與使用』,來降低類別或模組之間的耦合度 (i.e., 解耦)。
我的偶像 😍 軟體開發教父 —— Martin Fowler,因認為 “IoC” 的意義易使人困惑,
於 2000 年初,與多位 IoC 提倡者,給予其實作方式一個更具體的名稱
— — "Dependency Injection (依賴注入)"。
IoC 與 DI 常用來實現好萊塢原則、DIP 與開閉原則,能讓組件組裝與使用分離。許多框架提供 DI 容器,例如 Spring、Laravel 與 .NET 的生態系;但框架不必有容器才算框架,DI 也能在沒有容器時用單純的建構子或函式參數完成。
控制反轉 (Inversion of Control)
有去過 網咖 吧?
網咖,我們曾經的第二個家,有著歡笑、幹瞧、泡麵的桃源鄉。
還有多到哭的遊戲,
譬如:流星蝴蝶劍、天堂、RO、勁舞、世紀帝國、CS、淡水阿給、
三國、信長、守女、國王 TD、跑跑、惡靈勢力、GTA、LOL ⋯⋯ 。(青春再見 😭)
除了當天最新版更新、GGC 出包,或是網咖太爛
想請問: 熱門遊戲 有哪次是你自己下載的?
沒有,幾乎不用。
因為網咖都會提供好。
需要的 遊戲,不用自己 下載,而是 網咖提供 給你。 || 需要的 物件,不用自己 取得,而是 服務容器 提供 給你。 || 需要的 依賴實例,不用 主動 (Active) 建立,而是 被動 (Passive) 接收。
實例依賴物件 的 『控制流程 (Control Flow)』,由 主動 變成 被動。
就是 控制反轉 (Inversion of Control) 。
控制反轉 (Inversion of Control)vs
依賴反轉 (Dependency Inversion)
首先:
兩者不相等!
兩者不相等!
兩者不相等!
—— 覺得很重要 o.o
還記得唄?
依賴倒置原則 (Dependency Inversion Principle, DIP) :
- 高階模組不應該依賴於低階模組,兩者都該依賴抽象。
- 抽象不應該依賴於具體實作方式。
- 具體實作方式則應該依賴抽象。
倒轉的是 『依賴關係』。
控制反轉 (Inversion of Control, IoC),
倒轉的則是 實例依賴物件 的『控制流程』。
IoC 與 DIP 處理的是不同問題,常常互補,但沒有誰必須依附誰。你可以讓高階模組只依賴抽象,卻在 composition root 組裝具體實作;也可以使用 DI,卻仍把具體型別注入進去。真正要看的,是依賴是否指向穩定抽象,以及組裝責任是否放在合適位置。
如果違反 依賴倒置原則 ?
舉例來說,此程式違反了 依賴倒置原則,
它使高階模組 (Computer) 依賴 低階模組 (英雄聯盟):
class Computer {
// 依賴於低階模組:『具體』的英雄聯盟,而非 『抽象』的遊戲
private 英雄聯盟 lol;
public Computer() {
// 預設安裝遊戲: 英雄聯盟
lol = new 英雄聯盟();
}
public Computer(英雄聯盟 lol) {
this.lol = lol;
}
public void playGame() {
if (lol != null)
lol.play();
}
}
class 英雄聯盟 {
public void play() {
System.out.print("德瑪西雅~");
}
}
然而,他還是能透過 IoC/DI ,
被動取得 類別 “英雄聯盟” 的實例 lol:
解除了 高階模組 (Computer) 主動對 低階元件 (英雄聯盟) 的實例方式,
卻 解除不了 高階模組 對 低階模組 的 依賴關係。
因為 高階模組 依賴的是 具體實作 (英雄聯盟),
而非 抽象 (介面 or 抽象類別)。
如果想實現 依賴倒置原則 ?
還記得 依賴倒置原則 (Dependency Inversion Principle, DIP) 的結尾嗎?
僅僅將『 高階模組的依賴對象,由具體改為抽象 』,是 不夠 的,
因為高階模組 欲使用 低階模組的物件時,還是 需要自己 new 具體實作類別。
依賴並未解除
想解除這種依賴,即可透過 IoC/DI ,
直接將 所需低階元件 傳遞給 高階模組使用,
高階模組 啥都沒幹,就可以直接使用 低階模組。
真是
潮爽 der ~~
兩者結合後也就是:
高階模組,依賴於抽象,而非低階模組。
但 要使用 該抽象的 具體產品 (低階模組) 時,
- 不用也不需要知道是哪種 具體產品
- 不再自己實例 具體產品,而是 服務容器 會提供給他 。
好萊塢原則 (Hollywood Principle)
謎:一堆高階、低階、具體的,到底在公尛叮噹 😵。
好啦,不裝逼,講些實際的例子。
- 小碰友 不需要 自己賺錢,而是 爸媽 會給他。
- 我 不需要想 要吃什麼 早餐 和 自己去買,而是 女朋友 準備好給我。 (別告訴我,其實我沒有女朋友😭)
- 你 去廁所大便,不用想 要買哪牌 的衛生紙, 也不用自己準備 , 而是 廁所 提供給你。
- 我們 去網咖 ,不用想 要有哪些 遊戲,也不用自己下載,而是 網咖 會提供給你。
- … 。
怎樣,聽起來有沒有好方便,而且潮爽 der~ ?
由 服務容器 (IoC 容器) 透過 “依賴注入”,給予 高階模組 所需得具體產品:

取代傳統的主動建立實例:

[註]:為了避免造成誤會,
這邊指的 “建立” 是指 create,用 uml 來看也就是:

高階模組 不再去 "找尋" 插件,
而是插件會自己注入到 高階模組中。
這就是 好萊塢原則 (Hollywood Principle):
不要找我們,我們會找你。 (Don’t call us, we’ll call you.)
但是得知道:
DI 是讓組裝責任離開使用者的一種方式,不是 DIP 的同義詞。Factory 可以集中建立具體物件;Service Locator 則讓使用者在執行期查找服務,但會隱藏依賴、降低可測試性,不能因為用了它就宣稱已達成 DIP。
組裝位置通常稱為 composition root:它可以是應用程式入口、框架設定或容器設定,而不是每個高階模組都自己 new。
依賴注入 (Dependency Injection)
顧名思義:
將所需的 依賴實例,注入到高階模組中。
依賴注入並不神祕:把需要的合作對象從外部交給模組,而不是讓模組自己決定如何建立。常見形式有:
- 建構子注入 (Constructor Injection):適合必要依賴,通常最容易維持物件完整性與測試性。
- 設值方法注入 (Setter Injection):適合選用依賴或允許重新設定的情境。
- 介面注入 (Interface Injection):存在但在 Java 實務中較少見,通常不必為了 DI 額外引入一個 injector 介面。
這裡我以 “網咖” 做為舉例,
高階模組 (Computer) 依賴 抽象介面 (Game),
且有許多 具體產品 (Plugin) 實作此 抽象介面 (Game):
class Computer implements GameInjector {
private Game game; // 依賴 『抽象』,而非『具體』
// 建構子注入 (Constructor Injection)
public Computer(Game game) {
this.game = game;
}
// 設值方法注入 (Setter Injection)
public void setGame(Game game) {
this.game = game;
}
// 介面注入 (Interface Injection)
@Override
public void injectGame(Game game) {
this.game = game;
}
public void playGame() {
if (game != null) {
game.play();
}
}
}
// 遊戲注入者
// 可以規範: 任何需要 "遊戲" 的模組 都必須實做此介面
interface GameInjector{
void injectGame(Game game);
}
UML 類別圖:

可以看到程式中,沒有任何 “具體實作類別” 的名稱,
而是由 依賴注入 取得 插件實例,
高階模組,完全沒有與具體實作 耦合,
實現了 依賴倒置原則 (Dependency Inversion Principle, DIP) !
建構子注入 (Constructor Injection) vs 設值方法注入 (Setter Injection)
這兩種都很常見,那實際上用哪種方式最好呢?
Martin 說話了:
It’s important to support both mechanisms, even if there’s a preference for one of them.
—— Martin Fowler
即使你有較偏好的選擇,同時支持這兩種機制都是必要的。
IoC 容器 (IoC Container)
IoC 容器 (又稱: 服務容器 Service Container),
是 組裝 & 配置元件,透過 依賴注入 (Dependency Injection),
提供所需服務給模組的地方。
廣義上來說, IoC 容器,就是有進行 依賴注入 的地方,
你隨便寫一個類別,透過它將所需元件注入給 高階模組,便可說是容器。
但現在所說的 『容器』,往往是泛指那些強大『框架』的容器:
根據設定『自動生產』物件 (非單一產品),將其提供給所需模組,
並管理該物件整個生命週期的 超級自動化工廠。
大部分框架的 IoC 容器,幾乎都透過 『反射 (Reflection) 機制』,
來 動態生成實例、或由配置文件 (e.g., json、xml、properties、ini、php) 尋找依賴關係,
描述該如何建構實例、或檢查是否該為當前模組注入依賴 …。
因此容器,通常會有個 bind (或 register、config …etc.) 的函數,
供 註冊依賴關係、或告訴容器: 何種情境需要該實例。
再來,通常也會有有個 make (或 create、resolve …etc.),
讓容器 解析物件,或實例已綁定之物件。
網咖容器範例
接續以 "網咖" 作為例子,這是一個刻意簡化的容器示範;它省略生命週期、scope、錯誤處理與完整的型別解析,因此只適合閱讀基本組裝流程,不適合直接用於實務。
實際的容器,需要兼顧很多細節,
並且根據不同 語言、功能、用途,容器的寫法也不盡相同,
大部分框架都寫得很棒,我就不重造輪子了。
有興趣的再看即可,高手可以跳過 (非常簡單),
網咖容器範例 原始碼:
網咖容器範例 點我觀看
實際上的使用:
public class Main {
public static void main(String[] args) {
ServiceContainer container = new ServiceContainer(); // 實例 容器
// 註冊依賴關係: 當遇到 『 Game 』,讓容器給我 『 爆爆王 』
container.bind(Game.class, 爆爆王.class);
// 註冊依賴關係: 當遇到 『 Computer 』,讓容器給我 『 Computer(Game, 100) 』
// 並且 因為上面已註冊過 Game , 容器會直接傳回『 爆爆王 』給我
container.bind(Computer.class, Game.class, 100);
Computer computer = (Computer) container.make(Computer.class); // 讓容器製作 Computer
computer.playGame(); // 使用已被注入依賴的 Computer 物件
}
}
// Result:
/*
* 開始實例物件: Computer
* 建構子數量為4
*
* 尋訪 0 參數 建構子
* 未綁定0 參數
*
* 尋訪 1 參數 建構子
* 未綁定1 參數
*
* 尋訪 2 參數 建構子
* 尋訪 被綁定參數 list
* 參數型態: Game
* 進入 make 遞迴 實例參數
* 開始實例物件: Game
* 建構子數量為0
*
* 爆爆王 為 Game 的子類!
* ====實例物件====
* 爆爆王
* ===============
*
* 實例 Game 成功
*
* boolean
* Integer
* ===============
* 建構參數 與 綁定 list 數量相同,但型態不同 (多載)
* 略過此次迴圈尋訪
* ===============
*
* 尋訪 2 參數 建構子
* 尋訪 被綁定參數 list
*
* 參數型態: Game
* 進入 make 遞迴 實例參數
* 開始實例物件: Game
* 建構子數量為0
*
* 爆爆王 為 Game 的子類!
* ====實例物件====
* 爆爆王
* ===============
*
* 實例 Game 成功
*
* 此遊戲需要 100元
* 實例 Computer 成功
*
* 海盜船 14
*/
可以看到上面,範例中,
第一個參數,為要綁定的『條件』,
XXX.class ,是我做的簡易『型別提示』,
其他皆是 建構子的參數。
以 bind(Computer.class, Game.class,100) 為例,
第一個參數,為要綁定的『條件』,
後面都是建構子 參數。
但是,實際上的建構子,是長這樣:
public Computer(Game game, Integer money){...需傳入的,是 Game 實例,而非 Game.class,
容器會去解析 Game.class,看有沒有相關的 具體實作,幫你依賴注入。
再舉個例:
public class Main {
public static void main(String[] args) {
ServiceContainer container = new ServiceContainer(); // 實例 容器
// 註冊依賴關係: 當遇到 『 Game 』,讓容器給我 『 英雄聯盟 』
container.bind(Game.class, 英雄聯盟.class);
// 註冊依賴關係: 當遇到 『 Computer 』,讓容器給我 『 Computer(Game,true) 』
// 並且 因為上面已註冊過 Game , 容器會直接傳回『英雄聯盟』給我
container.bind(Computer.class, Game.class, true);
Computer computer1 = (Computer) container.make(Computer.class); // 讓容器製作 Computer
computer1.playGame(); // 使用已被注入依賴的 Computer 物件
}
}
// Result:
/*
* 開始實例物件: Computer
* 建構子數量為4
*
* 尋訪 0 參數 建構子
* 未綁定0 參數
*
* 尋訪 1 參數 建構子
* 未綁定1 參數
*
* 尋訪 2 參數 建構子
* 尋訪 被綁定參數 list
*
* 參數型態: Game
* 進入 make 遞迴 實例參數
* 開始實例物件: Game
* 建構子數量為0
*
* 英雄聯盟 為 Game 的子類!
* ====實例物件====
* 英雄聯盟
* ===============
*
* 實例 Game 成功
*
* 這是線上遊戲
* 實例 Computer 成功
*
* 德瑪西雅~
*/
總結
誒~ 不對啊
我只是要開發個 小系統,又不想學這個框架,
難道要自刻一個容器,才能使用 IoC/DI 嗎?
太鏘了吧 = =…
非也
上面有提到,不同語言、不同用途,容器寫法不盡相同,
所以我極度建議:
不要沒事造輪子
沒用框架也沒差,
要記得 『控制反轉 』指的是:
反轉『實例物件的控制流程』,
並非一定要有框架般的強大容器,才做得到,這是個錯誤的迷思~ 😮。
簡單加入一個 『第三方類別』進行實例元件、依賴注入,就可以達到。
這時 工廠方法模式 (Factory Method Pattern),就非常好用。
(再次強調,工廠 與 IoC/DI 並非互斥,甚至時常透過 工廠 實現 IoC/DI)
如果熟悉了 IoC/DI 的概念,
就知道了,學習框架,除了學習他的運作流程、提供的方法…,
再來就是學習,如何讓它 自動 調用你寫的類別,
這也是 框架 (Framework) 與 函式庫 (Library) 最大的差異之處。
範例原始檔
在〈控制反轉 (IoC) 與 依賴注入 (DI)〉中有 168 則留言
寫得真好
感恩 😬
不是寫程式的我也看不懂😂但以設計系的角度看,版面乾淨:))很清楚明瞭
哈哈 謝謝囉 😆
清晰易懂,例子也很有趣~學起來了
謝謝您 😁
我的榮幸
覺得棒棒der~
幽默又有深度的講解!!!已跪
Reflaction 應該改為 Reflection ?
感謝! 已修正
請問有推薦的好書嗎 或資料來源嗎? 或者能分享你自己理解這些東西的方法嗎? 謝謝
最佳地理解方式就是考察來源囉!
此外,就是常年使用各框架的些許心得吧 😄
IoC/DI 來自 Martin Fowler 此篇著名文章:
https://martinfowler.com/articles/injection.html
請問一下第二篇工廠模式的文章為什麼會刪掉? 謝謝
內容升級中 😂
補充更多實作細節~
(原文似乎過於簡易)
非常抱歉,造成您的不便😅
真的很棒 XD
以後可以出書了
以前似懂非懂的觀念被釐清了不少
真的很感謝您的鼓勵!😭
但還沒這麼厲害,可能要再好幾年 😂
寫太好 敲碗等更
感謝支持🙏🙏🙏
最近剛開始接觸這塊
一直搞不太懂
你的說明替我解惑了不少
太感謝了
很開心能幫到您 😇
觀念釐清了不少呢
請教一個問題,當高階模組達成DIP時是否也隱含了IoC/DI的概念呢? 因為當高階模組依賴抽象時代表它也new不出任何實例出來了,這時必定是等外部注入才能正常運作。請問我這樣的認知有錯誤嗎?
2020 年發現這篇文章,寫得很不錯
很清楚耶
排版也很乾淨
學到了 非常感謝!
神作
有些人說:
「工廠方法模式 (Factory Method Pattern) ,解除了 高階模組 與 低階模組的依賴,
但其缺點是,高階模組 必須實例其 具體實作工廠 的子類,
當擴充新產品時,還是必須修改 高階模組,因此違反了 開閉原則 (Open-Closed Principle)。」
恩…我們祝福他
想請教一下,這樣是有代表甚麼涵義嗎?
是指這個缺點不對嗎
看了都想回去打網咖了
reference
coconey porn
chastity humiliation
cheryl cole porn
prosexx
try not to cum game
old man porn gif
cock ninja porn
mummified bondage
naked gay
sugarbabe milf
naked tik tok
public agent full porn
porn thai
couger porn
gay cum dump
free bbc porn
cartoon monster porn
femdom.joi
enluxure
peachy boy porn
skyler mckay porn
reverse cowgirl anal
hentai double penetration
konulu porn
alessandra jane porn
ariella ferrera mom
femdom.joi
masturbating porn
naked tik tok
missax mom
hard spanking
brandi love lesbian
chinese femdom
xxx stepmom
videoteenage
big bum porn
shilpa sethi nude
metart gif
michelle thorne exposed
dainty wilder anal
daddy gay porn
shemale lesbian porn
free porn asian
milf sexting
90s pornstars
gay porn arab
hentai pantyhose
mikki marie nude
submitron rehearsal
shibari porn
hentai sissy
princess lexie joi
porn123
goth xxx
dilf porn
ava addams vr
temari hentai
fat mature porn
vibrator porn
free only fans porn
lady sonia fucking
alessandra jane porn
altyazılı porno izle
big ass pawg
free femboy porn
gay suit porn
gay bulge
做爱视频
سکس داستانی
lesbian mother porn
trans por
bdsm gangbang
big cock gay porn
lez porn
cuckold vr
vr orgy
gay bulge
turkce alt yazili porno
real gay porn
k pop demon hunters porn
harley quinn cosplay porn
salome munoz porn
kporn
nude lads
adam22 porn
jameliz sex tape
naomi soraya porn
public creampie
femdom worship
porn dildo
ornhu
rocco steele porn
erotica gif
nelporno
jenna jameson lesbian
yoga nude
selena star porn
reverse cowgirl anal
killergram porn
gloryhole hentai
cumshot porn
euporn
wank porn
stacy cruz porn
best cumshot
altyazılı porno izle
strawberry tabby porn
nelporno
milf hd porn
candy charms porn
british public porn
cuckhold porn
skinny teen pussy
charlie c naked
fuck my wife porn
gay joi
tiny cock
femdom worship
dick flash porn
pawg emily porn
czech porn casting
jill hardener porn
bum porn
video porno padre figlia
gay blowjobs
mollyredwolf
free elections betting sites usa
feminisation porn
wank videos
gay prison porn
shemale fuck girls
hd lesbian porn
fully clothed porn
gay dwarf porn
gayfuck
public masturbation
kporn
کیر تو کس