--- tags: SpringTracing, Spring, SpringBoot, SourceTracing, IoC, Inversion of Control --- # Spring 探索筆記 ~ Inversion of Control (2) ## 在開始研究之前 在開始研究Spring 之前有些前置的基礎需要優先建立,這會讓我們在探索Spring 時,較不會迷失方向。 * **IoC** * Factory Method Pattern * ApplicationContext ## IoC (Inversion of Control) 在Java的領域裡;IoC 是可以算得上是相當重要的觀念,尤其對於近幾年使用Spring-Boot開發Web Application的開發者來說,IoC的運用可以說是隨處可見。 但我相信大多數的新手開發者應該都跟我一樣,雖然會根據官方文件使用各種Spring-Boot的註解;但都僅把IoC當作Spring一個公式化的用法而已,不會去思考其含義是什麼。 因此在走進Spring Framework原始碼的世界前,我花了些時間理解了IoC的觀念,也希望藉此系統性地將Spring學好。 ## 定義 IoC 根據Wiki的定義,"*控制反轉(英語:Inversion of Control,縮寫為IoC),是物件導向程式設計中的一種設計原則,可以用來減低電腦代碼之間的耦合度。其中最常見的方式叫做**依賴注入**(Dependency Injection,簡稱DI),還有一種方式叫**依賴尋找**(Dependency Lookup)。*" 或許文字說明有些難懂,我們直接透過一個程式來理解較為直觀: ```java //設計一個遊戲主機 GameStation的類別 public class GameStation{ //建立遊戲實體 GameImage gImage = new GameImage("太空戰士"); //載入遊戲 public void play(){ this.gImage.load(); } } //設計一個遊戲GameImage的類別 public class GameImage { private String title; public GameImage(String gTitle) { this.title = gTitle; } public void load(){ System.out.println("開始遊戲>" + this.title); } } //主程式 public class Main { public static void main(String[] args) { GameStation gameStationIns = new GameStation(); gameStationIns.play(); } } ``` 不難發現,GameStation是一個糟透了的發明,GameStation不僅依賴了GameImage實體,還只能玩太空戰士一個遊戲。為了修改這個問題我相信大部分的開發者都能作出下列修正: ```java public class GameStation{ //外部遊戲的參考 GameImage gImage; //GameStation建構子 public GameStation(){ this.gImage = new GameImage(); } //GameStation建構子 (需預載遊戲) public GameStation(GameImage insertGImage){ this.gImage = insertGImage; } //替換 GameStation (需預載遊戲) public void setGameImage(GameImage insertGImage){ this.gImage = insertGImage; } //載入遊戲 public void play(){ this.gImage.load(); } } //主程式 public class Main { public static void main(String[] args) { GameImage gameImageIns = new GameImage("太空戰士"); GameImage gameImageIns2 = new GameImage("太空戰士2"); GameStation gameStationIns = new GameStation(gameImageIns); gameStationIns.play(); gameStationIns.setGameImage(gameImageIns2); gameStationIns.play(); } } ``` 如此的修正的確如"依賴注入"的基本想法一般,GameStation的依賴來源從原本的內部轉移至外部,我們現在可以玩來自外部不同的遊戲了,但這樣的修正難道就沒問題了嗎? 其實不是的,假設我今天有另一種新式的遊戲HDGameImage出現,那我GameStation還是一樣玩不了,因為事實上GameStation還是依賴了GameImage類的實體。 IoC的概念裡「高階模組不應該依賴於低階模組,兩者都該依賴抽象」,或是說「實現必須依賴抽象,而不是抽象依賴實現」,文字內的實現或是低階模組指的就是例子裡的GameImage這個類的實體,我們設計GameStation不應該去依賴GameImage這樣的實作類別,應該透過抽象。 怎麼檢視依賴行為? 其實如前段所說只要思考若需要引進一個新的實現 HDGameImage,我需不需要影響高階GameStation? 其實答案就一目了然了。 於是我們有了以下修正: ```java //設計一個可讀取的共用介面 public interface LoadableImage{ void load(); } //設計一個遊戲GameImage的類別 public class GameImage implements LoadableImage{ private String title; public GameImage(String gTitle) { this.title = gTitle; } public void load(){ System.out.println("開始遊戲>" + this.title); } } //設計一個HD遊戲HDGameImage的類別 public class HDGameImage implements LoadableImage{ private String title; public HDGameImage(String gTitle) { this.title = gTitle; } public void load(){ System.out.println("開始HD遊戲>" + this.title); } } //設計一個遊戲主機 GameStation的類別 public class GameStation{ //外部遊戲的參考 LoadableImage loadableImage; //GameStation建構子 public GameStation(){ this.loadableImage = new LoadableImage(){ public void load(){ System.out.println("無法讀取遊戲內容"); } }; } //GameStation建構子 (需預載遊戲) public GameStation(LoadableImage lImage){ this.loadableImage = lImage; } //替換 GameStation (需預載遊戲) public void setLoadableImage(LoadableImage lImage){ this.loadableImage = lImage; } //載入遊戲 public void play(){ this.loadableImage.load(); } } //主程式 public class Main { public static void main(String[] args) { GameImage gameImageIns = new GameImage("太空戰士"); HDGameImage gameImageIns2 = new HDGameImage("太空戰士2"); GameStation gameStationIns = new GameStation(); gameStationIns.play(); // 無法讀取遊戲內容 gameStationIns.setLoadableImage(gameImageIns); gameStationIns.play(); //開始遊戲>太空戰士 gameStationIns.setLoadableImage(gameImageIns2); gameStationIns.play(); //開始HD遊戲>太空戰士2 } } ``` 經過了設計之後我們的GameStation更實用且更有彈性。 希望上述例子可以讓大家理解IoC的概念以及演進,下一章節會開始研究什麼是Factory Method Pattern(工廠模式)。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up