用Delphi实现动态代理(2):设计说明
用Delphi實現動態代理(2):設計說明
[Mental Studio]猛禽[Blog]
在上篇《用Delphi實現動態代理(1):概述》中,對動態代理作了一個概要的說明,比如為什么需要這樣的動態代理,它有什么用等。本篇將對我實現的這個動態代理的設計思路作一下介紹。
?
一、設計目標
如上篇中這幅動態代理結構圖所示:
大致的設計目標有以下幾項:
- TMDynamicProxy可以將任意接口代理到一個通用接口IMInvocationHandler上;
- IMInvocationHandler的實現不能太復雜,即TMMethodInvocation的定義要盡量簡單;
- IMInvocationHandler要能夠實現Remoting,即TMMethodInvocation必須可序列化;
- 需要一個IMMethodInterceptor接口,以便于實現AOP所需要的各種攔截器;
- 需要一個TMInterfaceInvoker來把IMInvocationHandler轉為正確的對象調用。
從上面列出的目標可以看出,我的目標是要實現一套全新的多層框架,并且幾乎是對JAVA世界里最流行的輕量容器的模仿。接下來就說明一下原因所在。
?
二、原因
我之所以對這個動態代理如此熱情,源自于對DELPHI下多層技術的憤怒。想想從前在DELPHI開發多層應用有些什么?MIDAS?不可否認,MIDAS是一項很優秀的開發技術,可以在很大程度上簡化多層應用的開發。但是正因為它的簡單化,所以它跟RAD一樣,容易讓人在簡單化中迷失,而看不到問題的本質--對于這種事情,我喜歡引用老鄭鐘道新的一個經典比喻:它(比基尼泳裝)展示了令人感興趣的部分,但卻隱藏了關鍵的部分所顯示的是讓人感興趣的部分,所掩蓋的則是關鍵的部分(剛找到這句話的準確出處,特此糾正)。這導致的結果就是開發出大量的垃圾多層應用--至少跟大部分JAVA或CORBA多層應用相比是這樣的。
雖然李維寫了《Delphi 5.x多層分布式應用》系列三本書,試圖深入地解析一下MIDAS,但是一方面是在這浮躁的氛圍下大多數人沒有心思深入研究,另一方面則是MIDAS的先天不足。
上圖是我在2001年寫的一份書稿[1]里的一幅插圖,基本上可以說明MIDAS的整體結構,從圖上可以看出MIDAS沒有自己的基礎技術。
其中DCOM、Socket/TCP、Web/HTTP三種連接方式,本質上還是通過COM技術實現的。Socket和Web連接是借助于ScktSrvr和HttpSrvr兩個代理程序在服務端與客戶端之間建立一個Tunnel來繞過Windows的安全機制或是Firewall。在這樣的應用中,Remoting是靠DCOM的ORPC實現,Transactional Data Module服務端的事務處理、Pooling則是交由MTS/COM+ Container實現,而如果是用Remote Data Module/DCOM的話,則得不到這些方面的支持。
那么用CORBA連接的MIDAS呢?在后來版本的DELPHI中--大約是D5或D6開始--取消了這一支持。如果我沒記錯的話,在D4中的CORBA/MIDAS實現也是基于COM技術的。
最值得一提的是D6開始的SOAP支持,在這里,DELPHI終于提供了一個自己的Remoting實現,也是本文所要討論的動態代理。不過遺憾的是,這里用的動態代理并沒有被做成通用的東西,而是專門為SOAP實現定制了一個,與DELPHI的SOAP實現緊密捆綁。
正因為以上所說的原因,當Kylix出現時,我們面臨著在非Windows平臺下沒有MIDAS的困境。雖然這并非絕境,至少還可以用SOAP/MIDAS,或是純正的CORBA。遺憾的是SOAP的性能太差,而CORBA--且不說OMG沒有提供IDL2PAS的標準--還面臨著復雜性的問題。
?
三、為什么要模仿JAVA
我選擇JAVA作為模仿對象的一個原因是我認為:DELPHI需要一個像RMI這樣的Remoting技術。
目前流行的Remoting技術有兩類:
- 一是類似于CORBA/DCOM這樣的跨語言技術,這樣技術需要使用“代碼生成”--比如CORBA,需要先寫一個定義遠程對象的IDL文件,然后通過ORB提供的IDL編譯器來生成具體語言的Stub/Skeleton代碼;DCOM也類似,需要先寫一個MIDL(DELPHI中使用TypeLib Editor實現可視化編輯)來定義服務端對象,生成服務端接口代碼,編譯服務端程序時,MIDL也被編譯成TypeLib并以資源形式鏈接到服務端程序里;
- 另一種是類似于RMI這樣的獨有技術,Remoting的實現依賴于語言本身的動態特性或由平臺提供(如.net remoting)。比如RMI,將接口實現從RemoteObject派生即可為接口提供遠程訪問的語義。
顯然,RMI雖然犧牲了跨語言的優勢(其實RMI可以通過RMI over IIOP實現與CORBA的互操作,并沒有完全犧牲這一優勢),但同時也避免了“代碼生成”,這是一個很大的好處。
對于“代碼生成”這一惡行,我是深有體會的。去年我在試圖開發一個基于XML的WEB框架時,一直是用DELPHI提供的XML Data binding來做的。基本的做法是:先用工具(如XMLSPY)做好一個XML Schema(XSD),然后用XML Data binding wizard生成DELPHI的接口和類。當然,一旦生成就可以很方便地使用了,只要在程序里操作這些接口就好了,其中各個Field都會被變成屬性,并且類型也都如XSD中的定義。但問題在于程序在開發過程中,總是會有一些變化的,在這種情況下,就不得不同時開著XMLSPY修改XSD,然后重新用XML Data binding的Wizard跑一遍,非常的麻煩。做過CORBA開發的人大概也會有類似這樣的體會。雖然在DELPH下做COM開發很方便,有一個可視化的TypeLib Editor可以用,但是還是不爽。
當然,如果是在“理想”的“軟件藍領”環境里,那大概要幸福得多,因為交到Coder手里的接口已經是基本上定死,照著文檔做就是了,沒有這樣的煩惱。但對于Agiler們,這樣的麻煩是不可忍受的。
模仿JAVA的另一個原因是我對非侵入的輕量級容器的向往。所謂輕量級和重量級,是相對而言的,評價的標準就是系統對用戶代碼的侵入性大小。
先來看看一個侵入性強的例子。比如一個典型的MTS/COM+應用,用戶代碼除了要實現自己定義的業務邏輯接口以外,還至少必須要實現IObjectControl接口。另外,還必須通過ObjectContext與容器進行互動,比如通過IObjectContext接口進行基本的事務和安全性操作;如果要更進一步操作事務,還要通過ITransactionContext;要進行更多安全性操作,還要通過ISecurityProperty接口等。
可以看到,像事務處理、安全性檢查這些都是屬于基礎設施的部分,與用戶的業務邏輯沒有一點關系,但是這些部分又是“橫切”于用戶系統結構,與用戶代碼緊密交織在一起。而且更重要的是這些的控制權都掌握在容器框架手上,用戶只能按框架的要求來做,比如MTS/COM+的安全性控制就是與Windows的域用戶管理機制緊密捆綁。這帶來很大的學習成本,低的靈活性,以及開發的復雜性。
為此,專家們提出了AOP的思想。AOP即Aspect Oriented Programming,面向方面編程。但AOP并不是一種要取代OOP的新技術,而是對OOP處理“橫切”問題的一個補充。經典的OOP處理“橫切”問題通常是采用Template Method模式或類似的方法來解決,結果就是導致了強侵入性的框架。
基于AOP的思路,非侵入的框架應運而生。
一個典型的非侵入的框架中,用戶代碼始終是像下面這么簡潔明了:
// 業務邏輯定義{$M+}
IBizIntf = Interface
['{3A85E46D-F3D4-4D9C-A06C-4E7C1BAC9361}']
// 定義業務邏輯
End;
{$M-}
// 服務端業務邏輯實現
TBizImpl = class(TInterfacedObject, IBizIntf)
Protected
// 實現業務邏輯
end;
// 客戶端調用代碼
Var
BizObj : IBizIntf;
BizObj := TMDynamicProxy( TypeInfo( IBizIntf ), RemoteHandler ) As IBizIntf;
BizObj.xxx // 直接操作服務端對象
在這樣的應用中,業務邏輯定義是用DELPHI原生的接口,而不需要另外的像IDL或TypeLib之類,這樣整個開發過程都可以在DELPHI語言的范圍,用戶代碼的開發也可以將注意力完全集中在業務邏輯的處理上。
但光有AOP的思想是不夠的,還需要有實現。最初的實現是像AspectJ,在JAVA代碼編譯前作一次預編譯,將Aspect代碼織入后再用JAVA編譯器編譯成ByteCode。遺憾的是,這也是一種“代碼生成”技術。后來一個叫Jon Tirsen的人利用動態代理設計了一種運行時織入Aspect的實現--Naning[2]。
使用這種動態織入的Interceptor技術,框架提供的附加服務都可以通過Interceptor進行切入。不論是Remoting、事務、安全性,都可以做成相應的Interceptor,然后通過配置或代碼在運行時動態地將所需的Interceptor織入來實現所需要的功能。
而這一切的核心,還是“動態代理”。
參考文獻
[1]此書已經夭折,引用的插圖出自已經完成的第四章,在這里下載。
[2]透明《動態代理的前世今生》(《程序員》2005年第1期)
附:我用DELPHI實現的動態代理代碼可以在這里下載,還在改進中,僅供參考。(鏈接已更新)
[Mental Studio]猛禽 Feb.28-05, Mar.01
總結
以上是生活随笔為你收集整理的用Delphi实现动态代理(2):设计说明的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python长征路--单例模式
- 下一篇: 聚名:华为申请注册模盒商标 企业商标注册