| 251 | | == Dependency Injection == |
| 252 | | |
| 253 | | Spring 所採用的是Dependency Injection 來實現 IoC,中文翻譯為依賴注入 |
| 254 | | |
| 255 | | 依賴注入的意義是:「保留抽象介面,讓組件依賴於抽象介面,當組件要與其它實際的物件發生依賴關係時,藉過抽象介面來注入依賴的實際物件。」 |
| 256 | | |
| 257 | | 依賴注入在Martin Fowler的文章中談到了三種實現方式: |
| 258 | | * Interface injection (Type 1 IoC ) |
| 259 | | * Setter injection (Type 2 IoC ) |
| 260 | | * Constructor injection ( Type 3 IoC ) |
| 261 | | |
| 262 | | 上面的BusinessObject所實現的是Type 2 IoC,透過Setter注入依賴關係, |
| 263 | | |
| 264 | | == Type 2 IoC : Setter injection == |
| 265 | | |
| 266 | | BusinessObject 依賴於實際的 FloppyWriter,為了讓 BusinessObject 獲得重用性,不讓 BusinessObject 直接依賴於實際的 FloppyWriter,而是依賴於抽象的介面,複習一下 IoC 該節的例子(同上): |
| 267 | | {{{ |
| 268 | | #!java |
| 269 | | public interface IDeviceWriter { |
| 270 | | public void saveToDevice(); |
| 271 | | } |
| 272 | | |
| 273 | | public class BusinessObject { |
| 274 | | private IDeviceWriter writer; |
| 275 | | |
| 276 | | public void setDeviceWriter(IDeviceWriter writer) { |
| 277 | | this.writer = writer; |
| 278 | | } |
| 279 | | |
| 280 | | public void save() { |
| 281 | | .... |
| 282 | | writer.saveToDevice(); |
| 283 | | } |
| 284 | | } |
| 285 | | |
| 286 | | public class FloppyWriter implement IDeviceWriter { |
| 287 | | public void saveToDevice() { |
| 288 | | .... |
| 289 | | // 實際儲存至Floppy的程式碼 |
| 290 | | } |
| 291 | | } |
| 292 | | |
| 293 | | public class UsbDiskWriter implement IDeviceWriter { |
| 294 | | public void saveToDevice() { |
| 295 | | .... |
| 296 | | // 實際儲存至UsbDisk的程式碼 |
| 297 | | } |
| 298 | | } |
| 299 | | |
| 300 | | }}} |
| 301 | | |
| 302 | | 如果今天BusinessObject想要與UseDiskWriter物件發生依賴關係,可以這麼建立: |
| 303 | | {{{ |
| 304 | | #!java |
| 305 | | businessObject.setDeviceWriter(new UsbDiskWriter()); |
| 306 | | }}} |
| 307 | | |
| 308 | | 由於BusinessObject依賴於抽象介面,在需要建立依賴關係時,可以透過抽象介面注入依賴的實際物件。 |
| 309 | | |
| 310 | | == Type 3 IoC : Constructor injection == |
| 311 | | |
| 312 | | Type 3 IoC,則在是建構式上注入依賴關係,例如: |
| 313 | | {{{ |
| 314 | | #!java |
| 315 | | public class BusinessObject { |
| 316 | | private IDeviceWriter writer; |
| 317 | | |
| 318 | | public BusinessObject(IDeviceWriter writer) { |
| 319 | | this.writer = writer; |
| 320 | | } |
| 321 | | |
| 322 | | public void save() { |
| 323 | | .... |
| 324 | | writer.saveToDevice(); |
| 325 | | } |
| 326 | | } |
| 327 | | }}} |
| 328 | | |
| 329 | | Spring 鼓勵的是 Setter injection,但也允許您使用 Constructor injection,使用 Setter 或 Constructor 來注入依賴關係視您的需求而定,使用 Constructor 的好處之一是,您可以在建構物件的同時一併完成依賴關係的建立,然而如果要建立的物件關係很多,則會在建構式上留下一長串的參數,這時使用 Setter 會是個不錯的選擇,另一方面,Setter 可以有明確的名稱可以瞭解注入的物件會是什麼,像是setXXX()這樣的名稱會比記憶Constructor上某個參數位置代表某個物件來得好。 |
| 330 | | |
| 331 | | == Type 1 IoC : Interface injection == |
| 332 | | |
| 333 | | Type 1 IoC是Interface injection,使用Type 1 IoC時會要求實作介面,這個介面是為容器所用的,容器知道介面上所規定的方法,它可以呼叫實作介面的物件來完成依賴關係的注入,例如: |
| 334 | | {{{ |
| 335 | | #!java |
| 336 | | public interface IDependencyInjection { |
| 337 | | public void createDependency(Map dependObjects); |
| 338 | | } |
| 339 | | |
| 340 | | public class BusinessObject implement IDependencyInjection { |
| 341 | | private Map dependObjects; |
| 342 | | |
| 343 | | public void createDependency(Map dependObjects) { |
| 344 | | this.dependObject = dependObjects; |
| 345 | | // 在這邊實現與BusinessObject的依賴關係 |
| 346 | | ...... |
| 347 | | } |
| 348 | | |
| 349 | | public void save() { |
| 350 | | .... |
| 351 | | writer.saveToDevice(); |
| 352 | | } |
| 353 | | } |
| 354 | | |
| 355 | | }}} |
| 356 | | 如果要完成依賴關係注入的物件,必須實現IDependencyInjection介面,並交由容器管理,容器會呼叫被管理物件的createDependency()方法來完成依賴關係的建立。 |
| 357 | | |
| 358 | | 在上面的例子中,Type 1 IoC要求BusinessObject實現特定的介面,這就使得BusinessObject依賴於容器,如果日後BusinessObject要脫離目前這個容器,就必須修改程式,想想在更複雜的依賴關係中產生更多複雜的介面,組件與容器(框架)的依賴會更加複雜,最後使得組件無法從容器中脫離。 |
| 359 | | |
| 360 | | 所以Type 1 IoC具有強的侵入性,使用它來實現依賴注入會使得組件相依於容器(框架),降低組件的重用性。 |
| 361 | | |
| 362 | | Spring的核心是個IoC容器,您可以用Setter或Constructor的方式來實現您的業務物件,至於物件與物件之間的關係建立,則透過組態設定,讓Spring在執行時期根據組態檔的設定來為您建立物件之間的依賴關係,您不必特地撰寫一些Helper來自行建立這些物件之間的依賴關係,這不僅減少了大量的程式撰寫,也降低了物件之間的耦合程度。 |
| 363 | | |