C#墨攻IOC


原文叫看《墨攻》理解IOC概念

2006年多部賀歲大片以讓人應接不暇的頻率紛至沓來,其中張之亮的《墨攻》算是比較出彩的一部,講述了戰國時期墨家人革離幫助梁

國反抗趙國侵略的個人英雄主義故事,恢宏壯闊,渾雄凝重的歷史場面相當震撼。其中有一個場景:當劉德華所飾的墨者革離到達梁國都城

下,城上梁國守軍問:“來者何人?”,劉德華回答:“墨者革離!”,我們不妨用C#(原文是java,我修改)對這段“城門問對”的場景進行編劇並借由這個例子來理解IoC的內涵。

劇本和飾演者耦合

MoAttack代表《墨攻》的劇本,cityGetAsk()代表“城門問對”這段劇情,LiuDeHua是具體飾演者劉德華:

代碼清單1

public class MoAttack {

     public MoAttack() {}

     public void cityGateAsk(){

        LiuDeHua ldh = new LiuDeHua(); ① 演員直接侵入劇本

        ldh.responseAsk("墨者革離!");

   }

}

我們會發現以上劇本在①處,作為具體飾演者的劉德華直接侵入到劇本中,使劇本和演員直接耦合在一起:

圖(1)劇本與演員直接耦合

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

代碼清單2 MoAttack:引入劇本角色

public class MoAttack{

    public MoAttack() {}

    public void cityGateAsk()

    {

         GeLi geli = new LiuDeHua(); ① 引入革離角色接口

         geli.responseAsk("墨者革離!"); ② 通過接口開展劇情

     }

}

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

圖2劇本、革離、劉德華三者的類圖關系

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

圖3劇本和飾演者解耦了

通過引入導演,劇本和具體的飾演者解耦了,對應到軟件中,導演象是一個裝配器,將具體的飾演者賦給了劇本的角色。

現在我們可以反過來講解IOC的概念了。IOC(Inverse of Control)的字面意思是控制反轉,它包括兩個層面的內容:其一是“控制”,其二是“反轉”,到底是什么東西的控制被反轉了呢?對應到前面的例子, “控制”是指GeLi角色扮演者的選擇控制權,“反轉”是指這種選擇控制權從《墨攻》劇本中移除,轉交到導演的手中。對於程序來說,即是某一接口具體實現類的選擇控制權從客戶類中移除,轉交給第三方來確定,客戶類不知道是哪個具體的實現類,它通過接口方法對實現類進行調用。

因為IOC確實不夠開門見山,因此業界曾進行了廣泛的討論,最終軟件界的泰斗級人物Martin Fowler提出了DI(依賴注入:Dependency Injection)的概念,即將客戶類對接口實現類的依賴關系由第三方(容器或協作類)注入,以移除客戶類對具體接口實現類的依賴。“依賴注入”的概念顯然比“控制反轉”直接達意,易於理解。

IOC的三種類型

從注入方法上看,主要可以划分為三種的注入類型,分別是構造函數注入、屬性注入和接口注入,Spring.Net支持構造函數注入和屬性注入。下面我們繼續使用以上的例子說明這三種注入方法的區別。

構造函數注入

我們通過客戶類的構造函數,將接口實現類通過接口變量傳入,如代碼清單3所示:

代碼清單3 MoAttack:通過構造函數注入革離扮演者

public class MoAttack{

    public MoAttack(){}

    private GeLi geli;

    public MoAttack(GeLi geli){ ① 注入革離的具體扮演者

        this.geli = geli;

     }

     public void cityGateAsk()

     {

        geli.responseAsk(“墨者革離!”);

    }

}

MoAttack的構造函數不關心具體是誰扮演革離這個角色,只要在①處傳入的扮演者按劇本要求完成角色功能即可。

角色的具體扮演者由導演來安排,如代碼清單4所示:

代碼清單 4 Director:通過構造函數注入革離扮演者

public class Director {

     public void direct(){

        GeLi geli = new LiuDeHua(); ① 指定角色的扮演者

        MoAttack moAttack = new MoAttack(geli); ② 注入具體扮演者到劇本中

        moAttack.cityGateAsk();

    }

}

屬性注入

有時,導演會發現,雖然革離是影片《墨攻》的第一主人公,但並非每場戲都需要革離的出現,通過構造函數方式注入顯得很不妥當,在這種情況下,可以使用屬性注入進行改造。屬性注入通過通.Net 屬性完成客戶類所需依賴的注入,更靈活,更方便。

代碼清單5 MoAttack:通.Net 屬性器注入革離扮演者

public class MoAttack{

     private GeLi gelii;

    public GeLi Gelii{ ① 屬性注入方法

         set{ gelii = value; }

    }

    public void cityGateAsk() ...{

         geli.responseAsk("墨者革離");

    }

}

MoAttack在①處為geli 字段提供一個屬性,以便讓導演在拍需要革離的戲時才將注入geli的具體扮演者,而不需要劉德華從頭到尾跟着墨攻劇組跑。

代碼清單 6 Director:通過屬性注入革離扮演者

public class Director{

    public void direct(){

        GeLi geli = new LiuDeHua();

         MoAttack moAttack = new MoAttack();

        moAttack.Gelii = geli; ① 調用屬性注入

        moAttack.cityGateAsk();

    }

}

和通過構造函數注入革離扮演者不同,在實例化MoAttack時,並未指定任何扮演者,而是在實例化MoAttack后,調用其屬性注入扮演者。按照類似的方式,我們還可以為劇本中其他如巷淹中,梁王等角色分別提供注入的屬性,導演即可以根據所拍劇段的不同,注入所需要的角色了。

接口注入

將客戶類所有注入的方法抽取到一個接口中,客戶類通過實現這一接口提供注入的方法。為了采取接口注入的方式,需要聲明一個額外的接口:

public interface IActorArrangable{

     void injectGeli(GeLi geli);

}

然后,MoAttack實現這個接口並實現接口中的方法:

代碼清單7 MoAttack:通過接口方法注入革離扮演者

public class MoAttack : IActorArrangable{

    private GeLi geli;

    public void injectGeli (GeLi geli) { ① 實現接口方法

        this.geli = geli;

    }

     public void cityGateAsk() ...{

        geli.responseAsk("墨者革離");

     }

}

Director通過IActorArrangable接口的injectGeli()方法完成扮演者的注入工作。

代碼清單 8 Director:通過接口方法注入革離扮演者

public class Director{

     public void direct(){

        GeLi geli = new LiuDeHua();

        MoAttack moAttack = new MoAttack();

        moAttack.injectGeli (geli);

        moAttack.cityGateAsk();

    }

}

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

通過容器完成依賴關系的建立

雖然MoAttack和LiuDeHua實現了解耦,無需關注實現類的實例化工作,但這些工作在代碼中依然存在,只是轉移到Director中而已,導致導演的權力非常大,潛規則不斷滋生。假設某一制片人想改變這一局面,在相中某個劇本后,通過一個“海選”或者第三公正中介來選擇導演、演員,讓他們各司其職,那劇本、導演、演員就都實現解耦了。

所謂媒體“海選”和中介機構在程序領域即是一個第三方容器,它幫助我們完成類的初始化和裝配工作,讓我們從這些底層的實現類實例化,依賴關系的裝配中脫離出來,專注於更有意思的業務代碼的編寫工作,那確實是挺愜意的事情。Spring.Net就是這樣一個容器,它通過配置文件描述類之間的依賴關系,下面是Spring.Net配置文件的對以上實例進行配置的樣式代碼:

<objects>

    <object id="geli" type="com.baobaotao.LiuDeHua"></object>

    <object id="moAttack" type=" com.baobaotao.MoAttack">

        <property name="geli"><ref="geli"/></property>

    </object>

</objects>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM