控制反轉(zhuǎn)(Inverssion of Control,IoC)
原文地址:http://martinfowler.com/bliki/InversionOfControl.html
在擴(kuò)展框架的時(shí)候,我們常常會(huì)遇到“控制反轉(zhuǎn)”這個(gè)現(xiàn)象,確實(shí),它很多時(shí)候都被用來(lái)定義框架的特性。
現(xiàn)在我們來(lái)考慮一個(gè)簡(jiǎn)單的例子,假設(shè)我在寫(xiě)一個(gè)通過(guò)命令行的方式從用戶那里獲取信息的程序,我的程序會(huì)這樣寫(xiě):
#ruby
puts 'What is your name?'
name = gets
process_name(name)
puts 'What is your quest?'
quest = gets
process_quest(quest)
在交互的過(guò)程中,我的程序處于控制狀態(tài):它決定何時(shí)問(wèn)問(wèn)題、何時(shí)給出回應(yīng)、何時(shí)處理結(jié)果。
然而,如果我通過(guò)窗口的方式來(lái)做,那么我會(huì)通過(guò)配置窗口來(lái)實(shí)現(xiàn):
require 'tk'
root = TkRoot.new()
name_label = TkLabel.new() {text "What is Your Name?"}
name_label.pack
name = TkEntry.new(root).pack
name.bind("FocusOut") {process_name(name)}
quest_label = TkLabel.new() {text "What is Your Quest?"}
quest_label.pack
quest = TkEntry.new(root).pack
quest.bind("FocusOut") {process_quest(quest)}
Tk.mainloop()
在控制流程上,這兩個(gè)程序有很大的不同,尤其在控制方法 process_name 和 process_quest 的調(diào)用上。在命令行的形式中,我的程序決定何時(shí)調(diào)用這些方法;而在使用窗口的例子中,則不是由我的程序來(lái)決定,我把控制交給了窗口系統(tǒng)(通過(guò) Tk.mainloop 命令)。窗口系統(tǒng)基于我在創(chuàng)建窗口時(shí)所作的綁定來(lái)決定何時(shí)調(diào)用那些方法,所以控制被反轉(zhuǎn)了—不是有我的程序來(lái)調(diào)用窗口,而是由窗口來(lái)調(diào)用我的程序—這中現(xiàn)象就叫做“控制反轉(zhuǎn)”。
框架的一個(gè)很重要的特性就是,用戶定義方法常常由框架自身來(lái)調(diào)用,而不是由用戶的代碼來(lái)調(diào)用。框架常常扮演主程序的角色來(lái)協(xié)調(diào)應(yīng)用程序的活動(dòng)。這種控制反轉(zhuǎn)是框架成為一個(gè)強(qiáng)大的可以擴(kuò)展的骨架。用戶提供的方法對(duì)框架提供的一般性算法進(jìn)行裁剪來(lái)提供更特殊的應(yīng)用。--Ralph Johnson and Brian Foote
控制反轉(zhuǎn)是使框架不同于庫(kù)的一個(gè)關(guān)鍵的部分。庫(kù)在本質(zhì)上是一系列你可以調(diào)用的方法,現(xiàn)在一般組織成類(lèi)的形式,每次調(diào)用都會(huì)完成一些工作,然后將控制權(quán)返回給調(diào)用者。
框架內(nèi)部嵌套了一些抽象的設(shè)計(jì),有很多內(nèi)建表現(xiàn)。為了使用它,你需要通過(guò)繼承或者插件的方式將你的程序插入到框架的一些位置,然后框架會(huì)在這些地方調(diào)用你的代碼。
有多種方法向框架插入你需要被調(diào)用的代碼。在上面的例子中,我們通過(guò)在在文本框上調(diào)用 bind 方法,同時(shí)將消息名稱作為參數(shù)傳給它,后面再跟一個(gè)用“{}”括起來(lái)的方法,來(lái)向框架插入代碼。當(dāng)文本框檢測(cè)到一個(gè)消息的時(shí)候,就會(huì)調(diào)用在“{}”中的方法。使用“{}”是一中很方便的方式,但是很多編程語(yǔ)言都不支持這中方式。
另一種方法是框架定義一些消息,然后在用戶代碼中注冊(cè)這些消息。.NET就是這樣一個(gè)平臺(tái),它在語(yǔ)言方面擁有這樣的特性,可以讓用戶在窗口上定義一些消息,然后可以將方法綁定到這些消息上。
上面的這些方法(他們實(shí)際上是相同的)在單個(gè)case上面工作得很好,但是有時(shí)候需要在單個(gè)單元擴(kuò)展上面綁定多個(gè)方法的調(diào)用,在這種情況下,框架可以定義一個(gè)接口,用戶代碼通過(guò)實(shí)現(xiàn)這個(gè)接口來(lái)實(shí)現(xiàn)相關(guān)的調(diào)用。(這段話我也似懂非懂)
EJBs是這種形式的控制反轉(zhuǎn)的一個(gè)很好的例子。當(dāng)你在開(kāi)發(fā)一個(gè) session bean 的時(shí)候,你可以實(shí)現(xiàn)多個(gè)在不同生命周期點(diǎn)上被 EJB 容器調(diào)用的方法。例如,一個(gè) Session Bean 接口定義了 ejbRemove、ejbPassivate和ejbActivate。你不需要關(guān)心這些方法什么時(shí)候被調(diào)用,而只需要關(guān)心它們做些什么。由容器來(lái)調(diào)用用戶的程序,而不是用戶的程序來(lái)調(diào)用容器。
posted on 2007-04-10 15:50 ChenGen 閱讀(197) 評(píng)論(0) 編輯 收藏 所屬分類(lèi): spring