[轉]看部電影,透透徹徹理解IoC(你沒有理由再迷惑!)

来源:http://www.cnblogs.com/ji-yun/archive/2016/08/24/5804591.html
-Advertisement-
Play Games

之前對依賴註入的概念一直感到模糊,直到看了這篇文章:http://www.iteye.com/topic/1122835 引述: IoC(控制反轉:Inverse of Control)是Spring容器的內核,AOP、聲明式事務等功能在此基礎上開花結果。但是IoC這個重要的概念卻比較晦澀隱諱,不容 ...


之前對依賴註入的概念一直感到模糊,直到看了這篇文章:http://www.iteye.com/topic/1122835

引述

  IoC(控制反轉:Inverse of Control)是Spring容器的內核,AOP、聲明式事務等功能在此基礎上開花結果。但是IoC這個重要的概念卻比較晦澀隱諱,不容易讓人望文生義,這不能不說是一大遺憾。不過IoC確實包括很多內涵,它涉及代碼解耦、設計模式、代碼優化等問題的考量,我們打算通過一個小例子來說明這個概念。 

通過實例理解IoC的概念 

  賀歲大片在中國已經形成了一個傳統,每到年底總有多部賀歲大片紛至沓來讓人應接不暇。在所有賀歲大片中,張之亮的《墨攻》算是比較出彩的一部。該片講述了戰國時期墨家人革離幫助梁國反抗趙國侵略的個人英雄主義故事,恢巨集壯闊、渾雄凝重的歷史場面相當震撼。其中有一個場景:當劉德華所飾演的墨者革離到達梁國都城下,城上梁國守軍問到:“來者何人?”劉德華回答:“墨者革離!”我們不妨通過一個Java類為這個“城門叩問”的場景進行編劇,並藉此理解IoC的概念:

 MoAttack:通過演員安排劇本 :

public class MoAttack {  
   public void cityGateAsk(){  
        //①演員直接侵入劇本  
       LiuDeHua ldh = new LiuDeHua();  
       ldh.responseAsk("墨者革離!");  
   }  
}  

  我們會發現以上劇本在①處,作為具體角色飾演者的劉德華直接侵入到劇本中,使劇本和演員直接耦合在一起(圖3-1)。 

 

  一個明智的編劇在劇情創作時應圍繞故事的角色進行,而不應考慮角色的具體飾演者,這樣才可能在劇本投拍時自由地遴選任何適合的演員,而非綁定在劉德華一人身上。通過以上的分析,我們知道需要為該劇本主人公革離定義一個介面:

MoAttack:引入劇本角色 

public class MoAttack {  
   public void cityGateAsk()  
   {  
        //①引入革離角色介面  
       GeLi geli = new LiuDeHua();   
         
        //②通過介面開展劇情  
       geli.responseAsk("墨者革離!");    
   }  
}

  在①處引入了劇本的角色——革離,劇本的情節通過角色展開,在拍攝時角色由演員飾演,如②處所示。因此墨攻、革離、劉德華三者的類圖關係如圖 3 2所示: 

  可是,從圖3 2中,我們可以看出MoAttack同時依賴於GeLi介面和LiuDeHua類,並沒有達到我們所期望的劇本僅依賴於角色的目的。但是角色最終必須通過具體的演員才能完成拍攝,如何讓LiuDeHua和劇本無關而又能完成GeLi的具體動作呢?當然是在影片投拍時,導演將LiuDeHua安排在GeLi的角色上,導演將劇本、角色、飾演者裝配起來(圖3-3)。 

  通過引入導演,使劇本和具體飾演者解耦了。對應到軟體中,導演像是一個裝配器,安排演員表演具體的角色。 
  現在我們可以反過來講解IoC的概念了。IoC(Inverse of Control)的字面意思是控制反轉,它包括兩個內容: 

    • 其一是控制
    • 其二是反轉

  那到底是什麼東西的“控制”被“反轉”了呢對應到前面的例子,“控制”是指選擇GeLi角色扮演者的控制權;“反轉”是指這種控制權從《墨攻》劇本中移除,轉交到導演的手中。對於軟體來說,即是某一介面具體實現類的選擇控制權從調用類中移除,轉交給第三方決定。 
  因為IoC確實不夠開門見山,因此業界曾進行了廣泛的討論,最終軟體界的泰斗級人物Martin Fowler提出了DI(依賴註入:Dependency Injection)的概念用以代替IoC,即讓調用類對某一介面實現類的依賴關係由第三方(容器或協作類)註入,以移除調用類對某一介面實現類的依賴。“依賴註入”這個名詞顯然比“控制反轉”直接明瞭、易於理解。 

 IoC的類型

  從註入方法上看,主要可以劃分為三種類型:構造函數註入、屬性註入和介面註入。Spring支持構造函數註入和屬性註入。下麵我們繼續使用以上的例子說明這三種註入方法的區別。  

 構造函數註入 

  在構造函數註入中,我們通過調用類的構造函數,將介面實現類通過構造函數變數傳入,如代碼清單3-3所示: 

MoAttack:通過構造函數註入革離扮演者 

public class MoAttack {  
   private GeLi geli;  
   //①註入革離的具體扮演者  
   public MoAttack(GeLi geli){   
       this.geli = geli;  
   }  
    public void cityGateAsk(){  
       geli.responseAsk("墨者革離!");  
   }  
}  

  MoAttack的構造函數不關心具體是誰扮演革離這個角色,只要在①處傳入的扮演者按劇本要求完成相應的表演即可。角色的具體扮演者由導演來安排,如代碼清單3-4所示:  

Director:通過構造函數註入革離扮演者   

public class Director {  
   public void direct(){  
        //①指定角色的扮演者  
       GeLi geli = new LiuDeHua();    
  
        //②註入具體扮演者到劇本中  
       MoAttack moAttack = new MoAttack(geli);   
       moAttack.cityGateAsk();  
   }  
}  

   在①處,導演安排劉德華飾演革離的角色,併在②處,將劉德華“註入”到墨攻的劇本中,然後開始“城門叩問”劇情的演出工作。

 屬性註入 

   有時,導演會發現,雖然革離是影片《墨攻》的第一主角,但並非每個場景都需要革離的出現,在這種情況下通過構造函數註入相當於《墨攻》劇組每時每刻都要革離的飾演者在場,可見並不符合實際,這時可以考慮使用屬性註入。屬性註入可以有選擇地通過Setter方法完成調用類所需依賴的註入,意為著《墨攻》劇組可在橋段需要時讓革離的飾演者在場:   

MoAttack:通過Setter方法註入革離扮演者

public class MoAttack {  
    private GeLi geli;  
     //①屬性註入方法  
    public void setGeli(GeLi geli) {    
        this.geli = geli;  
    }  
    public void cityGateAsk() {  
        geli.responseAsk("墨者革離");  
    }  
}  

   MoAttack在①處為geli屬性提供一個Setter方法,以便讓導演在需要時註入geli的具體扮演者。 

 Director:通過Setter方法註入革離扮演者 

public class Director {  
   public void direct(){  
       GeLi geli = new LiuDeHua();  
       MoAttack moAttack = new MoAttack();  
  
        //①調用屬性Setter方法註入  
       moAttack.setGeli(geli);   
       moAttack.cityGateAsk();  
   }  
}  

  和通過構造函數註入革離扮演者不同,在實例化MoAttack劇本時,並未指定任何扮演者,而是在實例化MoAttack後,在需要革離出場時,才調用其setGeli()方法註入扮演者。按照類似的方式,我們還可以分別為劇本中其他諸如梁王、巷淹中等角色提供註入的Setter方法,這樣,導演就可以根據所拍劇段的不同,註入相應的角色了。 

介面註入 

  將調用類所有依賴註入的方法抽取到一個介面中,調用類通過實現該介面提供相應的註入方法。為了採取介面註入的方式,必須先聲明一個ActorArrangable介面: 

public interface ActorArrangable {  
   void injectGeli(GeLi geli);  
}  

  然後,MoAttack實現ActorArrangable介面提供具體的實現:  

MoAttack:通過介面方法註入革離扮演者

public class MoAttack implements ActorArrangable {  
    private GeLi geli;  
     //①實現介面方法  
    public void injectGeli (GeLi geli) {    
        this.geli = geli;         
    }  
    public void cityGateAsk() {  
        geli.responseAsk("墨者革離");  
    }  
}  

  Director通過ActorArrangable的injectGeli()方法完成扮演者的註入工作。  

Director:通過介面方法註入革離扮演者 

public class Director {  
   public void direct(){  
       GeLi geli = new LiuDeHua();  
       MoAttack moAttack = new MoAttack();  
       moAttack. injectGeli (geli);  
       moAttack.cityGateAsk();  
   }  
}  

    由於通過介面註入需要額外聲明一個介面,增加了類的數目,而且它的效果和屬性註入並無本質區別,因此我們不提倡採用這種方式。

通過容器完成依賴關係的註入 

   雖然MoAttack和LiuDeHua實現瞭解耦,MoAttack無須關註角色實現類的實例化工作,但這些工作在代碼中依然存在,只是轉移到Director類中而已。假設某一製片人想改變這一局面,在選擇某個劇本後,希望通過一個“海選”或者第三中介機構來選擇導演、演員,讓他們各司其職,那劇本、導演、演員就都實現解耦了。 
   所謂媒體“海選”和第三方中介機構在程式領域即是一個第三方的容器,它幫助完成類的初始化與裝配工作,讓開發者從這些底層實現類的實例化、依賴關係裝配等工作中脫離出來,專註於更有意義的業務邏輯開發工作。這無疑是一件令人嚮往的事情,Spring就是這樣的一個容器,它通過配置文件或註解描述類和類之間的依賴關係,自動完成類的初始化和依賴註入的工作。下麵是Spring配置文件的對以上實例進行配置的配置文件片斷: 

<?xml version="1.0" encoding="UTF-8" ?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:p="http://www.springframework.org/schema/p"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
        <!--①實現類實例化-->  
   <bean id="geli" class="LiuDeHua"/>  
   <bean id="moAttack" class="com.baobaotao.ioc.MoAttack"   
         p:geli-ref="geli"/><!--②通過geli-ref建立依賴關係-->  
</beans>  

   通過new XmlBeanFactory(“beans.xml”)等方式即可啟動容器。在容器啟動時,Spring根據配置文件的描述信息,自動實例化Bean並完成依賴關係的裝配,從容器中即可返回準備就緒的Bean實例,後續可直接使用之。 
   Spring為什麼會有這種“神奇”的力量,僅憑一個簡單的配置文件,就能魔法般地實例化並裝配好程式所用的Bean呢?這種“神奇”的力量歸功於Java語言本身的類反射功能。 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這裡想說一下在集合框架前需要理解的小知識點,也是個人的膚淺理解,不知道理解的正不正確,請大家多多指教。這裡必須談一下java的泛型,因為它們聯繫緊密,我們先看一下這幾行代碼: 這裡主要想測試一下這兩個類是不是相等的,根據我之前的認識,這應該是不相等的,但是運行輸出的結果是: class java.u ...
  • 八、BPMN 2.0流程圖詳解 BPMN 2.0的標準的出現是好事,用戶不在被某個工作流開發商綁架或者在工作流中開發妥協,Activiti作為BPMN標準的一套解決方案,使得用戶在選擇工作流框架時可以平滑的遷移過渡。也有負面的不好的消息,就是BPMN標準是大量開會討論和開發商妥協的結果(一般這是在做 ...
  • 前言: 好久沒有寫博客了,上次發表博客還是在5月的時候,主要是4月多入職的新公司,5月份就開始做項目一直忙到這個月的中旬項目上線才偷得浮生半日閑,但是項目上線後客戶還是隔三差五的提個問題,不是改BUG就是添加新的東西,本來想著一邊看編程思想一邊把每章的讀後感寫出來發出來的,但是天天忙著加班雖然也抽時 ...
  • 目前業界流行的分散式消息隊列系統(或者可以叫做消息中間件)種類繁多,比如,基於Erlang的RabbitMQ、基於Java的ActiveMQ/Apache Kafka、基於C/C++的ZeroMQ等等,都能進行大批量的消息路由轉發。它們的共同特點是,都有一個消息中轉路由節點,按照消息隊列裡面的專業術 ...
  • 1、視C++為一個語言聯邦 C、object-oriented C++、template C++、STL 2、儘可能使用const: 1)關鍵字const出現的星號左邊,表示被指物事常量;如果出現在星號右邊,表示指針自身是常量;如果出現在兩邊,表示被指物和指針都是常量。 2)stl迭代器中申明迭代器 ...
  • 1. Visual C++ Redistributable for Visual Studio 2015系統要求:Windows 7情況下必須是Windows 7 with SP1.或者Windows10 2.系統檢測方法:命令行輸入winver.exe回車,如果你的windows版本是7600,需 ...
  • Canvas 設置開始繪圖位置:ctx.moveTo(x,y); 設置直線到的位置:ctx.lineTo(x,y); 描邊繪製:ctx.stroke(); 填充繪製:ctx.fill(); 自動閉合路徑:ctx.closePath(); 開啟新的繪製:ctx.beginPath(); 設置描邊顏色: ...
  • 1. Dubbo是什麼? 只是一個框架 Hibernate是持久層框架,SpringMVC是MVC的框架,而Dubbo是分散式服務框架。 是框架而不是服務 所以不是像Tomcat或Memcached可以單獨啟動,它必須依附於應用才有意義。引入dubbo.jar的應用,並完成適合的配置後,這個應用就成 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...