級(jí)別: 中級(jí)
Bruce Tate (bruce.tate@j2life.com), 總裁, RapidRed
2006 年 12 月 18 日
靜態(tài)類型語言(如 Java? 語言和 C)可以在編譯時(shí)把方法調(diào)用綁定到其實(shí)現(xiàn)中。這項(xiàng)策略讓這類語言可以執(zhí)行更豐富的語法和類型檢查。比起不具有此項(xiàng)編譯時(shí)檢查功能的動(dòng)態(tài)類型語言來說,靜態(tài)類型語言更加穩(wěn)定且具有更佳的性能。然而靜態(tài)類型語言存在一個(gè)嚴(yán)重的局限性:前期綁定。一些動(dòng)態(tài)類型語言(如 Ruby、Smalltalk 和 Self)允許延遲綁定,它們可以實(shí)現(xiàn)另一個(gè)層次的編程功能。
幾年前,我有幸教我的大女兒學(xué)滑雪。滑雪學(xué)校提供的工具里有一條繩子,用這條繩把雪橇的尖端綁在一起。利用這根繩,初學(xué)滑雪的人能夠輕易地實(shí)現(xiàn)較為理想的滑雪動(dòng)作,如轉(zhuǎn)彎、減速和停止。最初,這些滑雪者十分依賴于這條繩子。我女兒還發(fā)誓說她離開這條繩就不滑雪。當(dāng)然,她這樣說是因?yàn)樗齽倓傞_始學(xué)所以對(duì)整個(gè)過程不了解。這沒關(guān)系。因?yàn)槲抑缹硭罱K會(huì)在滑雪時(shí)迫使自己沖破這一束縛。
 |
關(guān)于本系列
當(dāng)今,非 Java 框架正在影響著 Java 框架的構(gòu)建方式。您從其他語言學(xué)到的概念可以影響 Java 編程,您所編寫的 Python(或 Ruby、Smalltalk 等語言)代碼也可以改變編寫 Java 代碼的方式。因此,在 跨越邊界 系列文章中,作者 Bruce Tate 提出這樣一種觀點(diǎn):即通過學(xué)習(xí)其他框架方法和語言,Java 開發(fā)人員可以更好地武裝自己!
本專欄介紹與 Java 開發(fā)完全不同的編程概念和技術(shù),但這些概念和技術(shù)可以直接應(yīng)用于 Java 開發(fā)。在某些情況下,需要集成這些技術(shù)來利用它們;但在其他情況下,則可以直接應(yīng)用這些概念。在這里,單獨(dú)的工具并不重要,重要的是那些其他語言和框架中能夠影響 Java 社區(qū)開發(fā)人員、框架,甚至是基本方式的概念和思想。
|
|
作為一名 Java 開發(fā)人員,我也有過類似的經(jīng)歷。我喜愛靜態(tài)類型提供的安全性。我曾和 Dave Thomas 就靜態(tài)類型檢查的優(yōu)點(diǎn)這個(gè)問題進(jìn)行過辯論,但我卻不能被說服。如果沒有最初的這條“安全之繩”,滑雪會(huì)是一種截然不同的體驗(yàn)。對(duì)于許多人來說,靜態(tài)類型也是一種依賴,這和雪橇上的束帶沒有什么區(qū)別。Dave 認(rèn)為我只是還沒有足夠的經(jīng)歷來理解動(dòng)態(tài)語言的好處罷了。當(dāng)我認(rèn)識(shí)到 Ruby 和 Smalltalk 的妙處之后,我開始明白對(duì)動(dòng)態(tài)類型我的確了解得不夠,但這也加深了我對(duì)它的理解(參閱之前的 跨越邊界 系列,獲取對(duì)靜態(tài)類型策略和動(dòng)態(tài)類型策略的總體比較)。對(duì)我來說它最大的好處是延遲綁定。本文使用 Ruby、Smalltalk 和一個(gè)叫做 Self 的 Smalltalk 的派生語言的編程例子探討了延遲綁定的好處。
后期綁定和前期綁定
編程語言能夠?qū)?duì)函數(shù)(或在面向?qū)ο笳Z言中的方法)的聲明從其調(diào)用中分離出來??梢月暶饕粋€(gè)方法并使用單獨(dú)的語法調(diào)用這個(gè)方法,但最終系統(tǒng)需要將這兩者綁到一起。將調(diào)用和實(shí)現(xiàn)綁到一起的過程叫做綁定。前期先綁定到類型再綁定到實(shí)現(xiàn),還是后期先綁定到類型再綁定到實(shí)現(xiàn),這對(duì)一門給定語言的編程體驗(yàn)來說有著顯著的影響。大多數(shù)面向?qū)ο蟮恼Z言都在后期綁定到實(shí)現(xiàn),從而允許多態(tài)性 ,該功能讓您能夠?qū)⒃S多不同的子類型表示為一種類型。Java 代碼和 C 主要在前期的一個(gè)編譯的步驟里綁定到一種類型。使用此策略,編譯器就有足夠的信息可以捕獲許多不同類型的 bug,比如說方法參數(shù)或返回值之間類型的不兼容。例如,清單 1 中的代碼就會(huì)產(chǎn)生幾個(gè)編譯錯(cuò)誤:
清單 1. 編譯時(shí)綁定
...
x = 1.0 + anObject.methodThatReturnsString();
anObject.methodRequiringIntParameter(aString);
...
|
通過編譯時(shí)捕獲錯(cuò)誤,前期綁定有助于在提早檢查出問題。對(duì)這種協(xié)助的價(jià)值存在很多質(zhì)疑。很明顯,類型檢查并不足以證明代碼的正確性。必需經(jīng)過測(cè)試,而這種測(cè)試所發(fā)現(xiàn)的問題也常常與類型有關(guān)。但這種協(xié)助是有代價(jià)的。使用前期綁定,編譯器必需對(duì)目標(biāo)對(duì)象的結(jié)構(gòu)作出如下假設(shè):
- 必須有一個(gè)預(yù)定義好的有限方法列表。
- 想要調(diào)用的方法必須在編譯時(shí)存在。
- 必須綁定到已知的類型、接口或公共超類。
- 綁定目標(biāo)的方法和屬性必須是固定的。
這些假設(shè)所帶來的限制在最初也許并不明顯,但如果您再深入研究一下,很快就能發(fā)現(xiàn)后期綁定的益處,其中一些在跨越邊界 系列中已經(jīng)介紹過:
- Ruby on Rails 的持久性框架 Active Record 含有不具有編譯時(shí)屬性的對(duì)象。建立了一個(gè)數(shù)據(jù)庫連接后,Rails 在運(yùn)行時(shí)為數(shù)據(jù)庫中的每一行添加一個(gè)屬性到該模型。Active Record 用戶并不總需要隨著數(shù)據(jù)模型的改變而改變模型對(duì)象。正是后期綁定讓這一切成為可能。
- 單元測(cè)試員們必須經(jīng)常編寫代碼,這些代碼可以將測(cè)試實(shí)現(xiàn)(如 stub 或 mock)和產(chǎn)品實(shí)現(xiàn)綁定到相同的方法調(diào)用。Java 開發(fā)人員常使用叫做依賴性注入的設(shè)計(jì)模式來解決此問題,該模式常需要復(fù)雜的框架,如 EJB 或 Spring。這些框架雖然有很多優(yōu)勢(shì),但也帶來了相當(dāng)大的復(fù)雜性。面向方面的編程或有著依賴性查找的對(duì)象工廠也能解決該問題,但也同樣會(huì)增加復(fù)雜性。動(dòng)態(tài)語言中的測(cè)試框架(如 Smalltalk 和 Ruby)不需要依賴性注入框架,因?yàn)樗鼈兡軌蜻x擇在運(yùn)行時(shí)綁定到所需的實(shí)現(xiàn)。它們常常能夠用一小段代碼實(shí)現(xiàn)相同的目標(biāo)。
- 整個(gè) Smalltalk 語言是構(gòu)建在延遲綁定的前提之下的。Smalltalk 開發(fā)人員在一個(gè)叫做 image 的持續(xù)運(yùn)行的應(yīng)用程序之上構(gòu)建。因?yàn)樗傇谶\(yùn)行,對(duì)任何類中方法的任何添加、刪除或更新操作都發(fā)生在運(yùn)行時(shí)。延遲綁定讓 Smalltalk 應(yīng)用程序在整個(gè)開發(fā)周期中可以持續(xù)運(yùn)行。
連續(xù)區(qū)間
靜態(tài)或動(dòng)態(tài)只是連續(xù)區(qū)間中的點(diǎn)。一些語言高度靜態(tài)。Java 語言比 C 或 C++ 更為動(dòng)態(tài)。連續(xù)區(qū)間中的每個(gè)點(diǎn)都有一套自已的折衷方式。Java 語言有許多有助于延遲綁定的功能,這些功能都以相對(duì)較高的復(fù)雜度為代價(jià)。反射、依賴性注入以及 XML 配置都可用于延遲綁定和減少耦合。一直以來,Java 語言都是通過添加功能(如面向方面編程)來使其更為動(dòng)態(tài)的。您也許會(huì)認(rèn)為 Java 開發(fā)人員擁有了所需的一切。但還有一類語言 —— 如 Smalltalk、Self 和 Ruby —— 要比 Java 還要?jiǎng)討B(tài)且允許用更佳的方式來延遲綁定。
這些語言可以提供 Java 語言所沒有的技術(shù),如覆蓋在方法丟失時(shí)發(fā)生的行為。請(qǐng)記住,Java 語言需要存在用于編譯時(shí)綁定的方法。其他語言允許打開的類,這些類能夠基于開發(fā)人員需求進(jìn)行改變。如果您曾長久關(guān)注框架的發(fā)展,就會(huì)發(fā)現(xiàn)對(duì)延遲綁定的需求在日益增長,這種需求導(dǎo)致了 Java 語言中出現(xiàn)了許多很不自然的捆綁,它們使這門語言變得復(fù)雜且模糊。而其他語言則持觀望態(tài)度,等著我們?nèi)?gòu)建這類框架來實(shí)現(xiàn)更高的抽象級(jí)別以及更高的效率。對(duì)于您來說,好處很明顯:可以獲得一門更易表達(dá)且更高效的語言。
為了理解連續(xù)區(qū)間中的點(diǎn),可以看一下反射的情況。使用 Java 語言,可以在運(yùn)行時(shí)裝載類,通過反射找到一個(gè)方法,為該方法驗(yàn)證正確的參數(shù)設(shè)置,然后執(zhí)行該方法。要實(shí)現(xiàn)這些功能很可能需要編寫很多代碼行。但為延遲綁定所做出的這些努力常常會(huì)得不償失,所以大多數(shù) Java 應(yīng)用程序開發(fā)人員不會(huì)使用此項(xiàng)技術(shù)。Ruby、Smalltalk 和 Self 都使用一種原操作(如 Ruby 中的 object.send(method_name) )來完成此功能。該技術(shù)改變著這些語言中編程的本質(zhì),這樣的例子隨處可見。
對(duì)類型策略和綁定策略越是深入研究就越會(huì)發(fā)現(xiàn):等到運(yùn)行時(shí)再綁定到調(diào)用或類型會(huì)根本性地改變編程的過程,從而開啟一個(gè)全新的可能世界。沒錯(cuò),您會(huì)發(fā)現(xiàn)這樣不那么安全。但您也會(huì)發(fā)現(xiàn):重復(fù)少了、功能強(qiáng)大了并且在減少代碼行的同時(shí)有了更大的靈活性。為了理解這一切是如何運(yùn)行的,下面將快速介紹一下 Smalltalk、Self 和 Ruby。首先介紹延遲綁定調(diào)用方法,然后介紹一些可以在運(yùn)行時(shí)改變類定義的可用技術(shù)。
延遲調(diào)用
在靜態(tài)語言中,編譯器在編譯時(shí)直接將調(diào)用綁定到實(shí)現(xiàn)。動(dòng)態(tài)語言則有些不同。Ruby、Smalltalk 和 Self 依賴于消息傳送來延遲綁定。客戶機(jī)使用消息傳送來指定目標(biāo)對(duì)象、消息和參數(shù)集。這完全是一個(gè)運(yùn)行機(jī)制。所以動(dòng)態(tài)語言有效地添加了一級(jí)間接尋址。它們將消息名綁定到一個(gè)對(duì)象上,而不是從調(diào)用綁定到類型再到實(shí)現(xiàn)。然后,將該對(duì)象綁定到一個(gè)名稱或標(biāo)記,并使用該名稱或標(biāo)記在運(yùn)行時(shí)查找相關(guān)的實(shí)現(xiàn)。它是這樣工作的:
- 客戶機(jī)向目標(biāo)對(duì)象發(fā)送一條消息。
- 該消息有一個(gè)名稱和零個(gè)或多個(gè)參數(shù)。
- 該目標(biāo)(可以是類或?qū)ο螅┎檎沂欠裼信c此消息同名的方法。
- 如果有,目標(biāo)對(duì)象調(diào)用該方法。
- 如果沒有,目標(biāo)對(duì)象向父對(duì)象發(fā)送一條消息。父對(duì)象可能是一個(gè)超類(Smalltalk)、一個(gè)父對(duì)象(Self)或一個(gè)模塊(Ruby)。
- 如果在任何父對(duì)象中都沒找到該方法,會(huì)調(diào)用一個(gè)錯(cuò)誤捕捉方法。
上述所有步驟都發(fā)生在運(yùn)行時(shí) 。這意味著在執(zhí)行該消息的語句前,既不需要目標(biāo)方法,也不需要實(shí)現(xiàn)。在 Smalltalk 中,一切皆是對(duì)象,且大多數(shù)行為都利用消息傳送。甚至于控制結(jié)構(gòu)都依賴它。Smalltalk 有三種消息:一元消息(無參數(shù))、二元消息(帶固定的參數(shù)集)和關(guān)鍵字消息(帶已命名的參數(shù))。例如:
7 sin 將一元消息 sin 發(fā)送給目標(biāo)對(duì)象 7 。
3 + 4 是一條二元消息。它將帶參數(shù) 4 的 + 消息發(fā)送給對(duì)象 3 。
array at: 1 put "value". 是一條關(guān)鍵字消息。該代碼將消息 at: put: 發(fā)送到 array 對(duì)象,將 value 放置在數(shù)組中 1 的位置。
condition ifTrue: [doSomething] ifFalse: [doSomethingElse]. 是一條關(guān)鍵字消息。括號(hào)中的代碼叫做閉包或代碼塊 。這個(gè)代碼樣例將 :ifTrue :ifFalse 消息發(fā)送到 condition 對(duì)象。如果條件為真,Smalltalk 執(zhí)行 [doSomething] 代碼塊;否則執(zhí)行 [doSomethingElse] 代碼塊。
Ruby 支持消息傳送和直接方法調(diào)用。Ruby 中的消息傳送看起來有些許不同,但前提是一致的。在清單 2 中,定義了帶 speak 方法和 sleep 方法的 Dog 類。直接調(diào)用 speak 方法并通過 send 方法按 name 調(diào)用 sleep 方法。
清單 2. 在 Ruby 中用兩種方式調(diào)用方法
irb(main):001:0> class Dog
irb(main):002:1> def speak
irb(main):003:2> puts "Arf"
irb(main):004:2> end
irb(main):005:1> def sleep
irb(main):006:2> puts "Zzz"
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> dog = Dog.new
=> #<Dog:0x34fa9c>
irb(main):010:0> dog.speak
Arf
=> nil
irb(main):011:0> message = "sleep"
=> "sleep"
irb(main):012:0> dog.send message
Zzz
|
如果您是一名 Java 程序員,這些都不是什么新鮮內(nèi)容。您當(dāng)然可以通過 Java 的反射 API 處理后期綁定。只是需要更多的努力來實(shí)現(xiàn)同樣的功能。但用任意字符串(可以通過編程進(jìn)行修改)調(diào)用方法確實(shí)打開了語言中一項(xiàng)額外的功能。最為重要的是,能夠輕易地調(diào)用在運(yùn)行時(shí)過后所添加的任意行為。
Self 語言將消息傳送這一概念發(fā)揮到極致。在 Self 中,一切皆是對(duì)象。通過消息傳送專門地調(diào)用所有 Self 行為。Self 不含類(通過復(fù)制其他對(duì)象創(chuàng)建新對(duì)象)也不含變量(只有帶方法和對(duì)象的已命名的 slot)。Self 使用消息傳送來調(diào)用已命名的 slot 和方法。其他大多數(shù)面向?qū)ο蟮恼Z言通過允許直接訪問實(shí)例數(shù)據(jù)弱化了封裝的價(jià)值,但 Self 通過加強(qiáng)用于訪問方法和實(shí)例數(shù)據(jù)的相同協(xié)議克服了這一不足。發(fā)送一條消息調(diào)用一個(gè) slot。如果一個(gè) slot 有一個(gè)對(duì)象,該對(duì)象返回其值。Self 對(duì)訪問屬性和訪問方法不作區(qū)別。這項(xiàng)簡化措施讓 Self 成為了一門簡單但功能卻很強(qiáng)大的編程語言。像 Smalltalk 一樣,Self 用消息傳送來表示控制結(jié)構(gòu),Self 依賴于一個(gè)一直運(yùn)行的 image。Self 中的對(duì)象都有一個(gè)父對(duì)象以及包含其他對(duì)象或方法的 slot。從 Self 對(duì)消息傳送的嚴(yán)重依賴以及 Self 應(yīng)用程序一直運(yùn)行這一概念可以看出:延遲綁定是 Self 的中心課題。
Self 中的消息傳送和 Smalltalk 中的幾乎一樣。在 Smalltalk 中 count <- 3 將數(shù)字 3 賦給一個(gè)叫做 count 的變量。但 Self 沒有變量,也沒有賦值。需要發(fā)送消息 object count: 3 將 object 的 count slot 的值設(shè)置為 3 。要檢索 count 的值,只需簡單地使用 object count 。
當(dāng)您將 method_missing 加入進(jìn)來后,消息傳送就有了另一層次的意義。請(qǐng)記住,這項(xiàng)功能對(duì)于動(dòng)態(tài)語言來說是開放的,可對(duì)于在編譯時(shí)綁定到類型的語言來說卻是徹底關(guān)閉的。前期綁定的好處(強(qiáng)迫該方法必須存在)原來也是其核心的弱點(diǎn)。Ruby 讓您能夠越過 method_missing 行為來調(diào)用在運(yùn)行時(shí)也許不存在的方法的行為。Active Record 將類和表關(guān)聯(lián)起來,并按照數(shù)據(jù)庫中的每一列給每個(gè)類動(dòng)態(tài)地添加一個(gè)屬性。Active Record 也為每個(gè)列或列的集合自動(dòng)添加尋找程序!例如,映射到 people 數(shù)據(jù)庫表的 Person 類具有 first_name 和 last_name 列,person.find_by_first_name_and_last_name 是一個(gè)合法的 Active Record 語句,盡管這樣的方法并不存在。 Active Record 越過 method_missing 并在運(yùn)行時(shí)解析該方法名以確定該方法名是否有效。結(jié)果就會(huì)產(chǎn)生一個(gè)相當(dāng)有效的框架用于包裝數(shù)據(jù)庫表,該框架通過后期綁定而獲得了極大的簡化。但到目前為止,我還只是探討了有關(guān)調(diào)用的內(nèi)容?,F(xiàn)在可以討論一下有關(guān)添加行為的話題了。
在運(yùn)行時(shí)添加行為
所有這三種語言(Self、Smalltalk 和 Ruby)都使在運(yùn)行時(shí)添加行為變得十分簡單。使用 Self 和 Smalltalk,對(duì)現(xiàn)有類所做的任何更改都是通過定義一個(gè)運(yùn)行時(shí)修改來實(shí)現(xiàn)的。當(dāng)添加一個(gè)方法時(shí),也有效地修改了一個(gè)活動(dòng)的類。在 Self 中添加或刪除 slot 很簡單:只需要發(fā)送 _add_slot 消息。類似地,在 Smalltalk 中,可以通過調(diào)用相應(yīng)的消息(在一些地方稱作 compile )來添加方法或?qū)傩?。在這兩種情況下,都可以直接在 image 中修改類的單個(gè)副本。接下來我要對(duì) Ruby 中對(duì)類添加行為進(jìn)行稍微深入一點(diǎn)的探討。
Ruby 框架常使用在運(yùn)行時(shí)通過幾種不同的機(jī)制修改類的技術(shù)。最簡單的是打開類??梢源蜷_任何的 Ruby 類,并通過重命名、添加或刪除方法或?qū)傩詠砀乃<僭O(shè)您想要在 Ruby 中擴(kuò)展數(shù)字來簡化柱狀圖的實(shí)現(xiàn)過程。您可以打開 Fixnum 類,并添加一個(gè)方法來打印出相應(yīng)長度的柱,如清單 3 所示。
清單 3. 擴(kuò)展 Fixnum
irb(main):001:0> 7.class
=> Fixnum
irb(main):002:0> class Fixnum
irb(main):003:1> def bar
irb(main):004:2> puts(‘-‘*self)
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> 7
=> 7
irb(main):008:0> 7.bar
-------
=> nil
irb(main):009:0> [6, 8, 2, 4, 9].each {|i| i.bar}
------
--------
--
----
---------
=> [6, 8, 2, 4, 9]
|
這個(gè)例子展示了當(dāng)您在運(yùn)行程序時(shí)知道了該行為后該如何擴(kuò)展 Ruby 類。當(dāng)想要基于未知規(guī)則給一個(gè)類添加任意行為時(shí),則需要使用一項(xiàng)不同的技術(shù)??梢栽陬惖纳舷挛闹泄烙?jì)字符串。以清單 4 為例。清單 3 擴(kuò)展了 Fixnum 類,該類被認(rèn)為是靜態(tài)的。在清單 4 中,該類根本不需要被認(rèn)為是靜態(tài)的。對(duì) Dog 的擴(kuò)展讓您可以添加任意行為到 dog 或任何其他類中。該類打開自己,添加進(jìn)一個(gè)具有您指定的名稱的方法,并添加您指定給該類的行為:
清單 4. 可擴(kuò)展的類
class Dog
def self.extend(method_name, method_body)
class_eval(%Q[
def #{method_name}
#{method_body}
end
])
end
def speak
puts "Arf"
end
end
dog = Dog.new
Dog.extend("sneeze", "puts ‘Achoo!‘")
dog.speak
dog.sneeze
|
清單 4 中的 extend 方法還需要進(jìn)一步加以說明。這里大概地解釋一下。Ruby 打開一個(gè)類并添加一個(gè)帶有您提供給 Dog 類的名稱及主體的方法。首先,定義一個(gè)叫做 self.extend 的方法。self. 意味著 extend 是一個(gè)類方法,即在整個(gè)類上操作的方法,比如 new 方法。其次,調(diào)用 class_eval 。這個(gè)方法打開類并在打開的類上執(zhí)行下列字符串。接下來,所有在 %[ 和 ] 之間的代碼都被解釋為單個(gè)的字符串。最后,Ruby 按 #{ 和 } 之間的變量替代了該值。
現(xiàn)狀和超越
在清單 4 的例子中,從內(nèi)部擴(kuò)展了 Dog 。采用相同的技術(shù),可以用相同的方式擴(kuò)展任意的 Ruby 類?,F(xiàn)在,延遲綁定全部的能量就凸現(xiàn)了出來??梢詾橐粋€(gè)普通的類擴(kuò)展任意的功能并調(diào)用類的新行為,盡管當(dāng)編寫原始類時(shí)它們根本不存在。
反射的功能也不能不提。Self、Ruby 和 Smalltalk 通常都進(jìn)行反射。其消息傳送功能允許不迫使用戶訪問物理的方法就調(diào)用方法,正如在 Java 語言中那樣。class.methods 提供 Ruby 中一組方法名的數(shù)組,而 class methods 可以返回 Smalltalk 中的方法集。使用這些功能及類似的特性幾乎可以找到在類或?qū)ο笊线M(jìn)行快速自檢所需的一切。
到目前為止,我主要探討了關(guān)于綁定到方法的內(nèi)容,但延遲綁定的內(nèi)容要多得多??匆幌虑鍐?5 中所示的 Ruby 的方法定義。
清單 5. 添加兩個(gè)數(shù)字
如果采用類似的 Java 方法,就需要鍵入?yún)?shù)。這個(gè) Ruby 方法返回兩個(gè)數(shù)字的和。使用該方法惟一的要求是:第一個(gè)對(duì)象實(shí)現(xiàn) + 而第二個(gè)對(duì)象和第一個(gè)要兼容。該方法的客戶機(jī)能夠確定對(duì)象是否兼容。
類似的 Java 方法只適用于單一類型的參數(shù)。而這個(gè) Ruby 方法能夠服務(wù)于浮點(diǎn)型、整型、字符串型以及任何支持 + 方法的類型。延遲綁定可以讓單個(gè)的方法具有真正的多態(tài)性。該設(shè)計(jì)也是可擴(kuò)展的,因?yàn)樗軌蛑С之?dāng)前系統(tǒng)尚未實(shí)現(xiàn)的類型。例如,此方法可以輕易地支持虛數(shù)。
用 Java 語言延遲綁定
Java 社區(qū)對(duì)靜態(tài)類型檢查的迷戀程度令人驚訝,Java 程序員們正在不遺余力地尋找延遲綁定的方式。有些方法是成功的。諸如 Spring 等框架的存在主要是為了延遲綁定,它有助于減緩客戶機(jī)和服務(wù)之間的耦合。面向方面的編程通過提供能夠擴(kuò)展類的功能(甚至可以超出其當(dāng)前的功能)的服務(wù)來實(shí)現(xiàn)延遲綁定。像 Hibernate 這樣的框架也可以延遲綁定,通過反射、代理和其他工具在運(yùn)行時(shí)將持久性功能添加到純粹、普通的 Java 對(duì)象(POJO)中?,F(xiàn)在有很多關(guān)于如何用 POJO 編程的流行書籍可供開發(fā)人員參考,這些書籍大多會(huì)使用愈加復(fù)雜的技術(shù)(比反射還要先進(jìn)),而這些技術(shù)主要是為了打開類并延遲綁定,從而有效地回避了靜態(tài)類型。
在其他地方,延遲綁定的方法就不那么成功。依賴于 XML 來延遲綁定的部署描述符有很多問題。對(duì) XML 的過分依賴和我們對(duì)語言中的動(dòng)態(tài)行為的強(qiáng)烈渴望有很大關(guān)系,因?yàn)檫@些語言常常有點(diǎn)太過靜態(tài),綁定得有點(diǎn)太早,并且有點(diǎn)太受限制。
現(xiàn)在已經(jīng)有一些語言和技術(shù)可以為 Java 程序員們極想解決的這幾類問題提供解決方案,例如透明的持久性、為可測(cè)試性減少耦合、更加豐富的插件模型等。只要看看推動(dòng) Java 持久性框架發(fā)展的元程序設(shè)計(jì),并同 Active Record、Gemstone 或 Og(動(dòng)態(tài)語言中的持久性框架)中類似的解決方案對(duì)比一下,一切就一目了然了(參見 參考資料)。現(xiàn)在延遲綁定變得越來越重要,并且推動(dòng)該過程的那些思想和做法在其它語言中也甚為高效。當(dāng)您需要進(jìn)行元程序設(shè)計(jì)時(shí),請(qǐng)打開您的工具箱,加入幾種允許延遲綁定的語言。不要害怕跨越邊界!
參考資料
學(xué)習(xí)
獲得產(chǎn)品和技術(shù)
- Squeak:由 Disney 開發(fā)的一個(gè) Smalltalk 實(shí)現(xiàn)。
- Self:下載 Self 編程語言,該語言是 Smalltalk 的簡化版。
- Ruby on Rails:下載開源的 Ruby on Rails Web 框架。
- Ruby:從項(xiàng)目 Web 站點(diǎn)獲取 Ruby。
討論
|