<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 176, comments - 240, trackbacks - 0, articles - 7

    2008年7月7日

    謹(jǐn)以此文慶祝清華建校108周年,及5字班本科畢業(yè)20周年

    作者: Canonical

    眾所周知,計(jì)算機(jī)科學(xué)得以存在的基石是兩個(gè)基本理論:圖靈于1936年提出的圖靈機(jī)理論和丘奇同年早期發(fā)表的Lambda演算理論。這兩個(gè)理論奠定了所謂通用計(jì)算(Universal Computation)的概念基礎(chǔ),描繪了具有相同計(jì)算能力(圖靈完備),但形式上卻南轅北轍、大相徑庭的兩條技術(shù)路線。如果把這兩種理論看作是上帝所展示的世界本源面貌的兩個(gè)極端,那么是否存在一條更加中庸靈活的到達(dá)通用計(jì)算彼岸的中間路徑?

    自1936年以來,軟件作為計(jì)算機(jī)科學(xué)的核心應(yīng)用,一直處在不間斷的概念變革過程中,各類程序語言/系統(tǒng)架構(gòu)/設(shè)計(jì)模式/方法論層出不窮,但是究其軟件構(gòu)造的基本原理,仍然沒有脫出兩個(gè)基本理論最初所設(shè)定的范圍。如果定義一種新的軟件構(gòu)造理論,它所引入的新概念本質(zhì)上能有什么特異之處?能夠解決什么棘手的問題?

    本文中筆者提出在圖靈機(jī)和lambda演算的基礎(chǔ)上可以很自然的引入一個(gè)新的核心概念--可逆性,從而形成一個(gè)新的軟件構(gòu)造理論--可逆計(jì)算(Reversible Computation)。可逆計(jì)算提供了區(qū)別于目前業(yè)內(nèi)主流方法的更高層次的抽象手段,可以大幅降低軟件內(nèi)在的復(fù)雜性,為粗粒度軟件復(fù)用掃除了理論障礙。

    可逆計(jì)算的思想來源不是計(jì)算機(jī)科學(xué)本身,而是理論物理學(xué),它將軟件看作是處于不斷演化過程中的抽象實(shí)體, 在不同的復(fù)雜性層次上由不同的運(yùn)算規(guī)則所描述,它所關(guān)注的是演化過程中產(chǎn)生的微小差量如何在系統(tǒng)內(nèi)有序的傳播并發(fā)生相互作用。

    本文第一節(jié)將介紹可逆計(jì)算理論的基本原理與核心公式,第二節(jié)分析可逆計(jì)算理論與組件和模型驅(qū)動等傳統(tǒng)軟件構(gòu)造理論的區(qū)別和聯(lián)系,并介紹可逆計(jì)算理論在軟件復(fù)用領(lǐng)域的應(yīng)用,第三節(jié)從可逆計(jì)算角度解構(gòu)Docker、React等創(chuàng)新技術(shù)實(shí)踐。

    一. 可逆計(jì)算的基本原理

    可逆計(jì)算可以看作是在真實(shí)的信息有限的世界中,應(yīng)用圖靈計(jì)算和lambda演算對世界建模的一種必然結(jié)果,我們可以通過以下簡單的物理圖像來理解這一點(diǎn)。

    首先,圖靈機(jī)是一種結(jié)構(gòu)固化的機(jī)器,它具有可枚舉的有限的狀態(tài)集合,只能執(zhí)行有限的幾條操作指令,但是可以從無限長的紙帶上讀取和保存數(shù)據(jù)。例如我們?nèi)粘J褂玫碾娔X,它在出廠的時(shí)候硬件功能就已經(jīng)確定了,但是通過安裝不同的軟件,傳入不同的數(shù)據(jù)文件,最終它可以自動產(chǎn)生任意復(fù)雜的目標(biāo)輸出。圖靈機(jī)的計(jì)算過程在形式上可以寫成

    目標(biāo)輸出 = 固定機(jī)器(無限復(fù)雜的輸入)\\

    與圖靈機(jī)相反的是,lambda演算的核心概念是函數(shù),一個(gè)函數(shù)就是一臺小型的計(jì)算機(jī)器,函數(shù)的復(fù)合仍然是函數(shù),也就是說可以通過機(jī)器和機(jī)器的遞歸組合來產(chǎn)生更加復(fù)雜的機(jī)器。lambda演算的計(jì)算能力與圖靈機(jī)等價(jià),這意味著如果允許我們不斷創(chuàng)建更加復(fù)雜的機(jī)器,即使輸入一個(gè)常數(shù)0,我們也可以得到任意復(fù)雜的目標(biāo)輸出。lambda演算的計(jì)算過程在形式上可以寫成

    目標(biāo)輸出 = 無限復(fù)雜的機(jī)器(固定輸入)\\

    可以看出,以上兩種計(jì)算過程都可以被表達(dá)為Y=F(X) 這樣一種抽象的形式。如果我們把Y=F(X)理解為一種建模過程,即我們試圖理解輸入的結(jié)構(gòu)以及輸入和輸出之間的映射關(guān)系,采用最經(jīng)濟(jì)的方式重建輸出,則我們會發(fā)現(xiàn)圖靈機(jī)和lambda演算都假定了現(xiàn)實(shí)世界中無法滿足的條件。在真實(shí)的物理世界中,人類的認(rèn)知總是有限的,所有的量都需要區(qū)分已知的部分和未知的部分,因此我們需要進(jìn)行如下分解:

    \begin{align} Y &= F(X) \\   &= (F_0+F_1)(X_0+X_1)  \tag{拆分已知和未知} \\   &= F_0(X_0)+ △      \tag{類似Taylor級數(shù)展開} \end{align}

    重新整理一下符號,我們就得到了一個(gè)適應(yīng)范圍更加廣泛的計(jì)算模式

    Y=F(X)\oplus△\\

    除了函數(shù)運(yùn)算F(X)之外,這里出現(xiàn)了一個(gè)新的結(jié)構(gòu)運(yùn)算符⊕,它表示兩個(gè)元素之間的合成運(yùn)算,并不是普通數(shù)值意義上的加法,同時(shí)引出了一個(gè)新的概念:差量△。△的特異之處在于,它必然包含某種負(fù)元素,F(xiàn)(X)與△合并在一起之后的結(jié)果并不一定是“增加”了輸出,而完全可能是“減少”。

    在物理學(xué)中,差量△存在的必然性以及△包含逆元這一事實(shí)完全是不言而喻的,因?yàn)槲锢韺W(xué)的建模必須要考慮到兩個(gè)基本事實(shí):

    1. 世界是“測不準(zhǔn)”的,噪聲永遠(yuǎn)存在
    2. 模型的復(fù)雜度要和問題內(nèi)在的復(fù)雜度相匹配,它捕獲的是問題內(nèi)核中穩(wěn)定不變的趨勢及規(guī)律。

    例如,對以下的數(shù)據(jù) ​ ​

    我們所建立的模型只能是類似圖(a)中的簡單曲線,圖(b)中的模型試圖精確擬合每一個(gè)數(shù)據(jù)點(diǎn)在數(shù)學(xué)上稱之為過擬合,它難以描述新的數(shù)據(jù),而圖(c)中限制差量只能為正值則會極大的限制模型的描述精度。

    以上是對Y=F(X)⊕△這一抽象計(jì)算模式的一個(gè)啟發(fā)式說明,下面我們將介紹在軟件構(gòu)造領(lǐng)域落實(shí)這一計(jì)算模式的一種具體技術(shù)實(shí)現(xiàn)方案,筆者將其命名為可逆計(jì)算。 所謂可逆計(jì)算,是指系統(tǒng)化的應(yīng)用如下公式指導(dǎo)軟件構(gòu)造的一種技術(shù)路線

    App = Biz \quad aop\_extends \quad Generator\langle DSL\rangle \\

    App : 所需要構(gòu)建的目標(biāo)應(yīng)用程序
    DSL: 領(lǐng)域特定語言(Domain Specific Language),針對特定業(yè)務(wù)領(lǐng)域定制的業(yè)務(wù)邏輯描述語言,也是所謂領(lǐng)域模型的文本表示形式
    Generator : 根據(jù)領(lǐng)域模型提供的信息,反復(fù)應(yīng)用生成規(guī)則可以推導(dǎo)產(chǎn)生大量的衍生代碼。實(shí)現(xiàn)方式包括獨(dú)立的代碼生成工具,以及基于元編程(Metaprogramming)的編譯期模板展開
    Biz : 根據(jù)已知模型推導(dǎo)生成的邏輯與目標(biāo)應(yīng)用程序邏輯之間的差異被識別出來,并收集在一起,構(gòu)成獨(dú)立的差量描述
    aop_extends: 差量描述與模型生成部分通過類似面向切面編程(Aspect Oriented Programming)的技術(shù)結(jié)合在一起,這其中涉及到對模型生成部分的增加、修改、替換、刪除等一系列操作

    DSL是對關(guān)鍵性領(lǐng)域信息的一種高密度的表達(dá),它直接指導(dǎo)Generator生成代碼,這一點(diǎn)類似于圖靈計(jì)算通過輸入數(shù)據(jù)驅(qū)動機(jī)器執(zhí)行內(nèi)置指令。而如果把Generator看作是文本符號的替換生成,則它的執(zhí)行和復(fù)合規(guī)則完全就是lambda演算的翻版。差量合并在某種意義上是一種很新奇的操作,因?yàn)樗笪覀兙哂幸环N細(xì)致入微、無所不達(dá)的變化收集能力,能夠把散布系統(tǒng)各處的同階小量分離出來并合并在一起,這樣差量才具有獨(dú)立存在的意義和價(jià)值。同時(shí),系統(tǒng)中必須明確建立逆元和逆運(yùn)算的概念,在這樣的概念體系下差量作為“存在”與“不存在”的混合體才可能得到表達(dá)。

    現(xiàn)有的軟件基礎(chǔ)架構(gòu)如果不經(jīng)過徹底的改造,是無法有效的實(shí)施可逆計(jì)算的。正如圖靈機(jī)模型孕育了C語言,Lambda演算促生了Lisp語言一樣,為了有效支持可逆計(jì)算,筆者提出了一種新的程序語言X語言,它內(nèi)置了差量定義、生成、合并、拆分等關(guān)鍵特性,可以快速建立領(lǐng)域模型,并在領(lǐng)域模型的基礎(chǔ)上實(shí)現(xiàn)可逆計(jì)算。

    為了實(shí)施可逆計(jì)算,我們必須要建立差量的概念。變化產(chǎn)生差量,差量有正有負(fù),而且應(yīng)該滿足下面三條要求:

    1. 差量獨(dú)立存在
    2. 差量相互作用
    3. 差量具有結(jié)構(gòu)

    在第三節(jié)中筆者將會以Docker為實(shí)例說明這三條要求的重要性。

    可逆計(jì)算的核心是“可逆”,這一概念與物理學(xué)中熵的概念息息相關(guān),它的重要性其實(shí)遠(yuǎn)遠(yuǎn)超出了程序構(gòu)造本身,在可逆計(jì)算的方法論來源一文中,筆者會對它有更詳細(xì)的闡述。

    正如復(fù)數(shù)的出現(xiàn)擴(kuò)充了代數(shù)方程的求解空間,可逆計(jì)算為現(xiàn)有的軟件構(gòu)造技術(shù)體系補(bǔ)充了“可逆的差量合并”這一關(guān)鍵性技術(shù)手段,從而極大擴(kuò)充了軟件復(fù)用的可行范圍,使得系統(tǒng)級的粗粒度軟件復(fù)用成為可能。同時(shí)在新的視角下,很多原先難以解決的模型抽象問題可以找到更加簡單的解決方案,從而大幅降低了軟件構(gòu)造的內(nèi)在復(fù)雜性。在第二節(jié)中筆者將會對此進(jìn)行詳細(xì)闡述。

    軟件開發(fā)雖然號稱是知識密集性的工作,但到目前為止,眾多一線程序員的日常中仍然包含著大量代碼拷貝/粘貼/修改的機(jī)械化手工操作內(nèi)容,而在可逆計(jì)算理論中,代碼結(jié)構(gòu)的修改被抽象為可自動執(zhí)行的差量合并規(guī)則,因此通過可逆計(jì)算,我們可以為軟件自身的自動化生產(chǎn)創(chuàng)造基礎(chǔ)條件。筆者在可逆計(jì)算理論的基礎(chǔ)上,提出了一個(gè)新的軟件工業(yè)化生產(chǎn)模式NOP(Nop is nOt Programming),以非編程的方式批量生產(chǎn)軟件。NOP不是編程,但也不是不編程,它強(qiáng)調(diào)的是將業(yè)務(wù)人員可以直觀理解的邏輯與純技術(shù)實(shí)現(xiàn)層面的邏輯相分離,分別使用合適的語言和工具去設(shè)計(jì),然后再把它們無縫的粘接在一起。筆者將在另一篇文章中對NOP進(jìn)行詳細(xì)介紹。

    可逆計(jì)算與可逆計(jì)算機(jī)有著同樣的物理學(xué)思想來源,雖然具體的技術(shù)內(nèi)涵并不一致,但它們目標(biāo)卻是統(tǒng)一的。正如云計(jì)算試圖實(shí)現(xiàn)計(jì)算的云化一樣,可逆計(jì)算和可逆計(jì)算機(jī)試圖實(shí)現(xiàn)的都是計(jì)算的可逆化。

    二. 可逆計(jì)算對傳統(tǒng)理論的繼承和發(fā)展

    (一)組件(Component)

    軟件的誕生源于數(shù)學(xué)家研究希爾伯特第十問題時(shí)的副產(chǎn)品,早期軟件的主要用途也是數(shù)學(xué)物理計(jì)算,那時(shí)軟件中的概念無疑是抽象的、數(shù)學(xué)化的。隨著軟件的普及,越來越多應(yīng)用軟件的研發(fā)催生了面向?qū)ο蠛徒M件化的方法論,它試圖弱化抽象思維,轉(zhuǎn)而貼近人類的常識,從人們的日常經(jīng)驗(yàn)中汲取知識,把業(yè)務(wù)領(lǐng)域中人們可以直觀感知的概念映射為軟件中的對象,仿照物質(zhì)世界的生產(chǎn)制造過程從無到有、從小到大,逐步拼接組裝實(shí)現(xiàn)最終軟件產(chǎn)品的構(gòu)造。

    像框架、組件、設(shè)計(jì)模式、架構(gòu)視圖等軟件開發(fā)領(lǐng)域中耳熟能詳?shù)母拍睿苯觼碜杂诮ㄖI(yè)的生產(chǎn)經(jīng)驗(yàn)。組件理論繼承了面向?qū)ο笏枷氲木A,借助可復(fù)用的預(yù)制構(gòu)件這一概念,創(chuàng)造了龐大的第三方組件市場,獲得了空前的技術(shù)和商業(yè)成功,即使到今天仍然是最主流的軟件開發(fā)指導(dǎo)思想。但是,組件理論內(nèi)部存在著一個(gè)本質(zhì)性的缺陷,阻礙了它把自己的成功繼續(xù)推進(jìn)到一個(gè)新的高度。

    我們知道,所謂復(fù)用就是對已有的制成品的重復(fù)使用。為了實(shí)現(xiàn)組件復(fù)用,我們需要找到兩個(gè)軟件中的公共部分,把它分離出來并按照組件規(guī)范整理成標(biāo)準(zhǔn)形式。但是,A和B的公共部分的粒度是比A和B都要小的,大量軟件的公共部分是比它們中任何一個(gè)的粒度都要小得多的。這一限制直接導(dǎo)致越大粒度的軟件功能模塊越難以被直接復(fù)用,組件復(fù)用存在理論上的極限。可以通過組件組裝復(fù)用60%-70%的工作量,但是很少有人能超過80%,更不用說實(shí)現(xiàn)復(fù)用度90%以上的系統(tǒng)級整體復(fù)用了。

    為了克服組件理論的局限,我們需要重新認(rèn)識軟件的抽象本質(zhì)。軟件是在抽象的邏輯世界中存在的一種信息產(chǎn)品,信息并不是物質(zhì)。抽象世界的構(gòu)造和生產(chǎn)規(guī)律與物質(zhì)世界是有著本質(zhì)不同的。物質(zhì)產(chǎn)品的生產(chǎn)總是有成本的,而復(fù)制軟件的邊際成本卻可以是0。將桌子從房間中移走在物質(zhì)世界中必須要經(jīng)過門或窗,但在抽象的信息空間中卻只需要將桌子的坐標(biāo)從x改為-x而已。抽象元素之間的運(yùn)算關(guān)系并不受眾多物理約束的限制,因此信息空間中最有效的生產(chǎn)方式不是組裝,而是掌握和制定運(yùn)算規(guī)則。

    如果從數(shù)學(xué)的角度重新去解讀面向?qū)ο蠛徒M件技術(shù),我們會發(fā)現(xiàn)可逆計(jì)算可以被看作是組件理論的一個(gè)自然擴(kuò)展。

    • 面向?qū)ο?: 不等式 A > B
    • 組件 : 加法 A = B + C
    • 可逆計(jì)算 : 差量 Y = X + △Y

    面向?qū)ο笾械囊粋€(gè)核心概念是繼承:派生類從基類繼承,自動具有基類的一切功能。例如老虎是動物的一種派生類,在數(shù)學(xué)上,我們可以說老虎(A)這個(gè)概念所包含的內(nèi)容比動物(B)這個(gè)概念更多,老虎>動物(即A>B)。據(jù)此我們可以知道,動物這個(gè)概念所滿足的命題,老虎自然滿足, 例如動物會奔跑,老虎必然也會奔跑( P(B) -> P(A) )。程序中所有用到動物這一概念的地方都可以被替換為老虎(Liscov代換原則)。這樣通過繼承就將自動推理關(guān)系引入到軟件領(lǐng)域中來,在數(shù)學(xué)上這對應(yīng)于不等式,也就是一種偏序關(guān)系。

    面向?qū)ο蟮睦碚摾Ь吃谟诓坏仁降谋磉_(dá)能力有限。對于不等式A > B,我們知道A比B多,但是具體多什么,我們并沒有辦法明確的表達(dá)出來。而對于 A > B, D > E這樣的情況,即使多出來的部分一模一樣,我們也無法實(shí)現(xiàn)這部分內(nèi)容的重用。組件技術(shù)明確指出"組合優(yōu)于繼承",這相當(dāng)于引入了加法

    A=B+C\\ D=E+C

    這樣就可以抽象出組件C進(jìn)行重用。

    沿著上述方向推演下去,我們很容易確定下一步的發(fā)展是引入“減法”,這樣才可以把 A = B + C看作是一個(gè)真正的方程,通過方程左右移項(xiàng)求解出

    B=A-C=A+(-C)\\

    通過減法引入的“負(fù)組件”是一個(gè)全新的概念,它為軟件復(fù)用打開了一扇新的大門。

    假設(shè)我們已經(jīng)構(gòu)建好了系統(tǒng) X = D + E + F, 現(xiàn)在需要構(gòu)建 Y = D + E + G。如果遵循組件的解決方案,則需要將X拆解為多個(gè)組件,然后更換組件F為G后重新組裝。而如果遵循可逆計(jì)算的技術(shù)路線,通過引入逆元 -F, 我們立刻得到

    Y = X-F+G=X+(-F+G)=X+△Y\\

    在不拆解X的情況下,通過直接追加一個(gè)差量△Y,即可將系統(tǒng)X轉(zhuǎn)化為系統(tǒng)Y。

    組件的復(fù)用條件是“相同方可復(fù)用”,但在存在逆元的情況下,具有最大顆粒度的完整系統(tǒng)X在完全不改的情況下直接就可以被復(fù)用,軟件復(fù)用的范圍被拓展為“相關(guān)即可復(fù)用”,軟件復(fù)用的粒度不再有任何限制。組件之間的關(guān)系也發(fā)生了深刻的變化,不再是單調(diào)的構(gòu)成關(guān)系,而成為更加豐富多變的轉(zhuǎn)化關(guān)系

    Y = X + △Y 這一物理圖像對于復(fù)雜軟件產(chǎn)品的研發(fā)具有非常現(xiàn)實(shí)的意義。X可以是我們所研發(fā)的軟件產(chǎn)品的基礎(chǔ)版本或者說主版本,在不同的客戶處部署實(shí)施時(shí),大量的定制化需求被隔離到獨(dú)立的差量△Y中,這些定制的差量描述單獨(dú)存放,通過編譯技術(shù)與主版本代碼再合并到一起。主版本的架構(gòu)設(shè)計(jì)和代碼實(shí)現(xiàn)只需要考慮業(yè)務(wù)領(lǐng)域內(nèi)穩(wěn)定的核心需求,不會受到特定客戶處偶然性需求的沖擊,從而有效的避免架構(gòu)腐化。主版本研發(fā)和多個(gè)項(xiàng)目的實(shí)施可以并行進(jìn)行,不同的實(shí)施版本對應(yīng)不同的△Y,互不影響,同時(shí)主版本的代碼與所有定制代碼相互獨(dú)立,能夠隨時(shí)進(jìn)行整體升級。

    (二)模型驅(qū)動架構(gòu)(Model Driven Architecture)

    模型驅(qū)動架構(gòu)(MDA)是由對象管理組織(Object Management Group,OMG)在2001年提出的軟件架構(gòu)設(shè)計(jì)和開發(fā)方法,它被看作是軟件開發(fā)模式從以代碼為中心向以模型為中心轉(zhuǎn)變的里程碑,目前大部分所謂軟件開發(fā)平臺的理論基礎(chǔ)都與MDA有關(guān)。

    MDA試圖提升軟件開發(fā)的抽象層次,直接使用建模語言(例如Executable UML)作為編程語言,然后通過使用類似編譯器的技術(shù)將高層模型翻譯為底層的可執(zhí)行代碼。在MDA中,明確區(qū)分應(yīng)用架構(gòu)和系統(tǒng)架構(gòu),并分別用平臺無關(guān)模型PIM(Platform Independent Model)和平臺相關(guān)模型PSM(Platform Specific Model)來描述它們。PIM反映了應(yīng)用系統(tǒng)的功能模型,它獨(dú)立于具體的實(shí)現(xiàn)技術(shù)和運(yùn)行框架,而PSM則關(guān)注于使用特定技術(shù)(例如J2EE或者dotNet)實(shí)現(xiàn)PIM所描述的功能,為PIM提供運(yùn)行環(huán)境。

    使用MDA的理想場景是,開發(fā)人員使用可視化工具設(shè)計(jì)PIM,然后選擇目標(biāo)運(yùn)行平臺,由工具自動執(zhí)行針對特定平臺和實(shí)現(xiàn)語言的映射規(guī)則,將PIM轉(zhuǎn)換為對應(yīng)的PSM,并最終生成可執(zhí)行的應(yīng)用程序代碼。基于MDA的程序構(gòu)造可以表述為如下公式

    App=Transformer(PIM)\\

    MDA的愿景是像C語言取代匯編那樣最終徹底消滅傳統(tǒng)編程語言。但經(jīng)歷了這么多年發(fā)展之后,它仍未能夠在廣泛的應(yīng)用領(lǐng)域中展現(xiàn)出相對于傳統(tǒng)編程壓倒性的競爭優(yōu)勢。

    事實(shí)上,目前基于MDA的開發(fā)工具在面對多變的業(yè)務(wù)領(lǐng)域時(shí),總是難掩其內(nèi)在的不適應(yīng)性。根據(jù)本文第一節(jié)的分析,我們知道建模必須要考慮差量。而在MDA的構(gòu)造公式中,左側(cè)的App代表了各種未知需求,而右側(cè)的Transformer和PIM的設(shè)計(jì)器實(shí)際上都主要由開發(fā)工具廠商提供,未知=已知這樣一個(gè)方程是無法持久保持平衡的。

    目前,工具廠商的主要做法是提供大而全的模型集合,試圖事先預(yù)測用戶所有可能的業(yè)務(wù)場景。但是,我們知道“天下沒有免費(fèi)的午餐”,模型的價(jià)值在于體現(xiàn)了業(yè)務(wù)領(lǐng)域中的本質(zhì)性約束,沒有任何一個(gè)模型是所有場景下都最優(yōu)的。預(yù)測需求會導(dǎo)致出現(xiàn)一種悖論: 模型內(nèi)置假定過少,則無法根據(jù)用戶輸入的少量信息自動生成大量有用的工作,也無法防止用戶出現(xiàn)誤操作,模型的價(jià)值不明顯,而如果反之,模型假定很多,則它就會固化到某個(gè)特定業(yè)務(wù)場景,難以適應(yīng)新的情況。

    打開一個(gè)MDA工具的設(shè)計(jì)器,我們最經(jīng)常的感受是大部分選項(xiàng)都不需要,也不知道是干什么用的,需要的選項(xiàng)卻到處找也找不到。

    可逆計(jì)算對MDA的擴(kuò)展體現(xiàn)為兩點(diǎn):

    1. 可逆計(jì)算中Generator和DSL都是鼓勵用戶擴(kuò)充和調(diào)整的,這一點(diǎn)類似于面向語言編程(Language-oriented programming)
    2. 存在一個(gè)額外的差量定制機(jī)會,可以對整體生成結(jié)果進(jìn)行精確的局部修正。

    在筆者提出的NOP生產(chǎn)模式中,必須要包含一個(gè)新的關(guān)鍵組件:設(shè)計(jì)器的設(shè)計(jì)器。普通的程序員可以利用設(shè)計(jì)器的設(shè)計(jì)器快速設(shè)計(jì)開發(fā)自己的領(lǐng)域特定語言(DSL)及其可視化設(shè)計(jì)器,同時(shí)可以通過設(shè)計(jì)器的設(shè)計(jì)器對系統(tǒng)中的任意設(shè)計(jì)器進(jìn)行定制調(diào)整,自由的增加或者刪除元素。

    (三)面向切面編程(Aspect Oriented Programming)

    面向切面(AOP)是與面向?qū)ο螅∣OP)互補(bǔ)的一種編程范式,它可以實(shí)現(xiàn)對那些橫跨多個(gè)對象的所謂橫切關(guān)注點(diǎn)(cross-cutting concern)的封裝。例如,需求規(guī)格中可能規(guī)定所有的業(yè)務(wù)操作都要記錄日志,所有的數(shù)據(jù)庫修改操作都要開啟事務(wù)。如果按照面向?qū)ο蟮膫鹘y(tǒng)實(shí)現(xiàn)方式,需求中的一句話將會導(dǎo)致眾多對象類中陡然膨脹出現(xiàn)大量的冗余代碼,而通過AOP, 這些公共的“修飾性”的操作就可以被剝離到獨(dú)立的切面描述中。這就是所謂縱向分解和橫向分解的正交性。


    AOP本質(zhì)上是兩個(gè)能力的組合:

    1. 在程序結(jié)構(gòu)空間中定位到目標(biāo)切點(diǎn)(Pointcut)
    2. 對局部程序結(jié)構(gòu)進(jìn)行修改,將擴(kuò)展邏輯(Advice)編織(Weave)到指定位置。

    定位依賴于存在良好定義的整體結(jié)構(gòu)坐標(biāo)系(沒有坐標(biāo)怎么定位?),而修改依賴于存在良好定義的局部程序語義結(jié)構(gòu)。目前主流的AOP技術(shù)的局限性在于,它們都是在面向?qū)ο蟮恼Z境下表達(dá)的,而領(lǐng)域結(jié)構(gòu)與對象實(shí)現(xiàn)結(jié)構(gòu)并不總是一致的,或者說用對象體系的坐標(biāo)去表達(dá)領(lǐng)域語義是不充分的。例如,申請人和審批人在領(lǐng)域模型中是需要明確區(qū)分的不同的概念,但是在對象層面卻可能都對應(yīng)于同樣的Person類,使用AOP的很多時(shí)候并不能直接將領(lǐng)域描述轉(zhuǎn)換為切點(diǎn)定義和Advice實(shí)現(xiàn)。這種限制反映到應(yīng)用層面,結(jié)果就是除了日志、事務(wù)、延遲加載、緩存等少數(shù)與特定業(yè)務(wù)領(lǐng)域無關(guān)的“經(jīng)典”應(yīng)用之外,我們找不到AOP的用武之地。

    可逆計(jì)算需要類似AOP的定位和結(jié)構(gòu)修正能力,但是它是在領(lǐng)域模型空間中定義這些能力的,因而大大擴(kuò)充了AOP的應(yīng)用范圍。特別是,可逆計(jì)算中領(lǐng)域模型自我演化產(chǎn)生的結(jié)構(gòu)差量△能夠以類似AOP切面的形式得到表達(dá)。

    我們知道,組件可以標(biāo)識出程序中反復(fù)出現(xiàn)的“相同性”,而可逆計(jì)算可以捕獲程序結(jié)構(gòu)的“相似性”。相同很罕見,需要敏銳的甄別,但是在任何系統(tǒng)中,有一種相似性都是唾手可得的,即動力學(xué)演化過程中系統(tǒng)與自身歷史快照之間的相似性。這種相似性在此前的技術(shù)體系中并沒有專門的技術(shù)表達(dá)形式。

    通過縱向和橫向分解,我們所建立的概念之網(wǎng)存在于一個(gè)設(shè)計(jì)平面當(dāng)中,當(dāng)設(shè)計(jì)平面沿著時(shí)間軸演化時(shí),很自然的會產(chǎn)生一個(gè)“三維”映射關(guān)系:后一時(shí)刻的設(shè)計(jì)平面可以看作是從前一時(shí)刻的平面增加一個(gè)差量映射(定制)而得到,而差量是定義在平面的每一個(gè)點(diǎn)上的。這一圖像類似于范疇論(Category Theory)中的函子(Functor)概念,可逆計(jì)算中的差量合并扮演了函子映射的角色。因此,可逆計(jì)算相當(dāng)于擴(kuò)展了原有的設(shè)計(jì)空間,為演化這一概念找到了具體的一種技術(shù)表現(xiàn)形式。

    (四)軟件產(chǎn)品線(Software Product Line)

    軟件產(chǎn)品線理論源于一個(gè)洞察,即在一個(gè)業(yè)務(wù)領(lǐng)域中,很少有軟件系統(tǒng)是完全獨(dú)特的,大量的軟件產(chǎn)品之間存在著形式和功能的相似性,可以歸結(jié)為一個(gè)產(chǎn)品家族,把一個(gè)產(chǎn)品家族中的所有產(chǎn)品(已存在的和尚未存在的)作為一個(gè)整體來研究、開發(fā)、演進(jìn),通過科學(xué)的方法提取它們的共性,結(jié)合有效的可變性管理,就有可能實(shí)現(xiàn)規(guī)模化、系統(tǒng)化的軟件復(fù)用,進(jìn)而實(shí)現(xiàn)軟件產(chǎn)品的工業(yè)化生產(chǎn)。

    軟件產(chǎn)品線工程采用兩階段生命周期模型,區(qū)分領(lǐng)域工程應(yīng)用工程。所謂領(lǐng)域工程,是指分析業(yè)務(wù)領(lǐng)域內(nèi)軟件產(chǎn)品的共性,建立領(lǐng)域模型及公共的軟件產(chǎn)品線架構(gòu),形成可復(fù)用的核心資產(chǎn)的過程,即面向復(fù)用的開發(fā)(development for reuse)。而應(yīng)用工程,其實(shí)質(zhì)是使用復(fù)用來開發(fā)( development with reuse),也就是利用已經(jīng)存在的體系架構(gòu)、需求、測試、文檔等核心資產(chǎn)來制造具體應(yīng)用產(chǎn)品的生產(chǎn)活動。

    卡耐基梅隆大學(xué)軟件工程研究所(CMU-SEI)的研究人員在2008年的報(bào)告中宣稱軟件產(chǎn)品線可以帶來如下好處:

    1. 提升10倍以上生產(chǎn)率
    2. 提升10倍以上產(chǎn)品質(zhì)量
    3. 縮減60%以上成本
    4. 縮減87%以上人力需求
    5. 縮減98%以上產(chǎn)品上市時(shí)間
    6. 進(jìn)入新市場的時(shí)間以月計(jì),而不是年

    軟件產(chǎn)品線描繪的理想非常美好:復(fù)用度90%以上的產(chǎn)品級復(fù)用、隨需而變的敏捷定制、無視技術(shù)變遷影響的領(lǐng)域架構(gòu)、優(yōu)異可觀的經(jīng)濟(jì)效益等等。它所存在的唯一問題就是如何才能做到?盡管軟件產(chǎn)品線工程試圖通過綜合利用所有管理的和技術(shù)的手段,在組織級別策略性的復(fù)用一切技術(shù)資產(chǎn)(包括文檔、代碼、規(guī)范、工具等等),但在目前主流的技術(shù)體制下,發(fā)展成功的軟件產(chǎn)品線仍然面臨著重重困難。

    可逆計(jì)算的理念與軟件產(chǎn)品線理論高度契合,它的技術(shù)方案為軟件產(chǎn)品線的核心技術(shù)困難---可變性管理帶來了新的解決思路。在軟件產(chǎn)品線工程中,傳統(tǒng)的可變性管理主要是適配、替換和擴(kuò)展這三種方式:


    這三種方式都可以看作是向核心架構(gòu)補(bǔ)充功能。但是可復(fù)用性的障礙不僅僅是來自于無法追加新的功能,很多時(shí)候也在于無法屏蔽原先已經(jīng)存在的功能。傳統(tǒng)的適配技術(shù)等要求接口一致匹配,是一種剛性的對接要求,一旦失配必將導(dǎo)致不斷向上傳導(dǎo)應(yīng)力,最終只能通過整體更換組件來解決問題。可逆計(jì)算通過差量合并為可變性管理補(bǔ)充了“消除”這一關(guān)鍵性機(jī)制,可以按需在領(lǐng)域模型空間中構(gòu)建出柔性適配接口,從而有效的控制變化點(diǎn)影響范圍。

    可逆計(jì)算中的差量雖然也可以被解釋為對基礎(chǔ)模型的一種擴(kuò)展,但是它與插件擴(kuò)展技術(shù)之間還是存在著明顯的區(qū)別。在平臺-插件這樣的結(jié)構(gòu)中,平臺是最核心的主體,插件依附于平臺而存在,更像是一種補(bǔ)丁機(jī)制,在概念層面上是相對次要的部分。而在可逆計(jì)算中,通過一些形式上的變換,我們可以得到一個(gè)對稱性更高的公式:

     A = B \oplus G(D) \equiv (B,D) \\

    如果把G看作是一種相對不變的背景知識,則形式上我們可以把它隱藏起來,定義一個(gè)更加高級的“括號”運(yùn)算符,它類似于數(shù)學(xué)中的“內(nèi)積”。在這種形式下,B和D是對偶的,B是對D的補(bǔ)充,而D也是對B的補(bǔ)充。同時(shí),我們注意到G(D)是模型驅(qū)動架構(gòu)的體現(xiàn),模型驅(qū)動之所以有價(jià)值就在于模型D中發(fā)生的微小變化,可以被G放大為系統(tǒng)各處大量衍生的變化,因此G(D)是一種非線性變換,而B是系統(tǒng)中去除D所對應(yīng)的非線性因素之后剩余的部分。當(dāng)所有復(fù)雜的非線性影響因素都被剝離出去之后,最后剩下的部分B就有可能是簡單的,甚至能夠形成一種新的可獨(dú)立理解的領(lǐng)域模型結(jié)構(gòu)(可以類比聲波與空氣的關(guān)系,聲波是空氣的擾動,但是不用研究空氣本體,我們就可以直接用正弦波模型來描述聲波)。

    A = (B,D)的形式可以直接推廣到存在更多領(lǐng)域模型的情況

     A = (B,D,E,F, ...) \\

    因?yàn)锽、D、E等概念都是某種DSL所描述的領(lǐng)域模型,因此它們可以被解釋為A投影到特定領(lǐng)域模型子空間所產(chǎn)生的分量,也就是說,應(yīng)用A可以被表示為一個(gè)“特征向量”(Feature Vector), 例如

    應(yīng)用A = (Biz,權(quán)限,流程,報(bào)表,...) \\

    與軟件產(chǎn)品線中常用的面向特征編程(Feature Oriented Programming)相比,可逆計(jì)算的特征分解方案強(qiáng)調(diào)領(lǐng)域特定描述,特征邊界更加明確,特征合成時(shí)產(chǎn)生的概念沖突更容易處理。

    特征向量本身構(gòu)成更高維度的領(lǐng)域模型,它可以被進(jìn)一步分解下去,從而形成一個(gè)模型級列,例如定義

    D' \equiv [B,D]\ G'(D')\equiv B\oplus G(D)\\

    , 并且假設(shè)D'可以繼續(xù)分解

    D'=V\oplus M(U)=M'(U') \\

    ,則可以得到

    \begin{align} A &= B ⊕ G(D) \\   &= G'(D') \\  &= G'(M'(U'))\\  &= G'(M'([V,U])) \\  \end{align} \\

    最終我們可以通過領(lǐng)域特征向量U'來描述D’,然后再通過領(lǐng)域特征向量D‘來描述原有的模型A。

    可逆計(jì)算的這一構(gòu)造策略類似于深度神經(jīng)網(wǎng)絡(luò),它不再局限于具有極多可調(diào)參數(shù)的單一模型,而是建立抽象層級不同、復(fù)雜性層級不同的一系列模型,通過逐步求精的方式構(gòu)造出最終的應(yīng)用。

    在可逆計(jì)算的視角下,應(yīng)用工程的工作內(nèi)容變成了使用特征向量來描述軟件需求,而領(lǐng)域工程則負(fù)責(zé)根據(jù)特征向量描述來生成最終的軟件。

    三. 初露端倪的差量革命

    (一)Docker

    Docker是2013年由創(chuàng)業(yè)公司dotCloud開源的應(yīng)用容器引擎,它可以將任何應(yīng)用及其依賴的環(huán)境打包成一個(gè)輕量級、可移植、自包含的容器(Container),并據(jù)此以容器為標(biāo)準(zhǔn)化單元創(chuàng)造了一種新型的軟件開發(fā)、部署和交付形式。

    Docker一出世就秒殺了Google的親兒子lmctfy (Let Me Contain That For You)容器技術(shù),同時(shí)也把Google的另一個(gè)親兒子Go語言迅速捧成了網(wǎng)紅,之后Docker的發(fā)展 更是一發(fā)而不可收拾。2014年開始一場Docker風(fēng)暴席卷全球,以前所未有的力度推動了操作系統(tǒng)內(nèi)核的變革,在眾多巨頭的跟風(fēng)造勢下瞬間引爆容器云市場,真正從根本上改變了企業(yè)應(yīng)用從開發(fā)、構(gòu)建到部署、運(yùn)行整個(gè)生命周期的技術(shù)形態(tài)。

    Docker技術(shù)的成功源于它對軟件運(yùn)行時(shí)復(fù)雜性的本質(zhì)性降低,而它的技術(shù)方案可以看作是可逆計(jì)算理論的一種特例。Docker的核心技術(shù)模式可以用如下公式進(jìn)行概括

    App = Docker\langle Dockerfile\rangle \quad unionfs \quad BaseImage\\

    Dockerfile是構(gòu)建容器鏡像的一種領(lǐng)域特定語言,例如

    FROM ubuntu:16.04 
    RUN useradd --user-group --create-home --shell /bin/bash work
    RUN apt-get update -y && apt-get install -y python3-dev
    COPY . /app RUN make /app
    ENV PYTHONPATH /FrameworkBenchmarks
    CMD python /app/app.py
    EXPOSE 8088

    通過Dockerfile可以快速準(zhǔn)確的描述容器所依賴的基礎(chǔ)鏡像,具體的構(gòu)建步驟,運(yùn)行時(shí)環(huán)境變量和系統(tǒng)配置等信息。

    Docker應(yīng)用程序扮演了可逆計(jì)算中Generator的角色,負(fù)責(zé)解釋Dockerfile,執(zhí)行對應(yīng)的指令來生成容器鏡像。

    創(chuàng)造性的使用聯(lián)合文件系統(tǒng)(Union FS),是Docker的一個(gè)特別的創(chuàng)新之處。這種文件系統(tǒng)采用分層的構(gòu)造方式,每一層構(gòu)建完畢后就不會再發(fā)生改變,在后一層上進(jìn)行的任何修改都只會記錄在自己這一層。例如,修改前一層的文件時(shí)會通過Copy-On-Write的方式復(fù)制一份到當(dāng)前層,而刪除前一層的文件并不會真的執(zhí)行刪除操作,而是僅在當(dāng)前層標(biāo)記該文件已刪除。Docker利用聯(lián)合文件系統(tǒng)來實(shí)現(xiàn)將多個(gè)容器鏡像合成為一個(gè)完整的應(yīng)用,這一技術(shù)的本質(zhì)正是可逆計(jì)算中的aop_extends操作。

    Docker的英文是碼頭搬運(yùn)工人的意思,它所搬運(yùn)的容器也經(jīng)常被人拿來和集裝箱做對比:標(biāo)準(zhǔn)的容器和集裝箱類似,使得我們可以自由的對它們進(jìn)行傳輸/組合,而不用考慮容器中的具體內(nèi)容。但是這種比較是膚淺的,甚至是誤導(dǎo)性的。集裝箱是靜態(tài)的、簡單的、沒有對外接口的,而容器則是動態(tài)的、復(fù)雜的、和外部存在著大量信息交互的。這種動態(tài)的復(fù)雜結(jié)構(gòu)想和普通的靜態(tài)物件一樣封裝成所謂標(biāo)準(zhǔn)容器,其難度不可同日而語。如果沒有引入支持差量的文件系統(tǒng),是無法構(gòu)建出一種柔性邊界,實(shí)現(xiàn)邏輯分離的。

    Docker所做的標(biāo)準(zhǔn)封裝其實(shí)虛擬機(jī)也能做到,甚至差量存儲機(jī)制在虛擬機(jī)中也早早的被用于實(shí)現(xiàn)增量備份,Docker與虛擬機(jī)的本質(zhì)性不同到底在什么地方?回顧第一節(jié)中可逆計(jì)算對差量三個(gè)基本要求,我們可以清晰的發(fā)現(xiàn)Docker的獨(dú)特之處。

    1. 差量獨(dú)立存在:Docker最重要的價(jià)值就在于通過容器封裝,拋棄了作為背景存在(必不可少,但一般情況下不需要了解),占據(jù)了99%的體積和復(fù)雜度的操作系統(tǒng)層。應(yīng)用容器成為了可以獨(dú)立存儲、獨(dú)立操作的第一性的實(shí)體。輕裝上陣的容器在性能、資源占用、可管理性等方面完全超越了虛胖的虛擬機(jī)。
    2. 差量相互作用:Docker容器之間通過精確受控的方式發(fā)生相互作用,可通過操作系統(tǒng)的namespace機(jī)制選擇性的實(shí)現(xiàn)資源隔離或者共享。而虛擬機(jī)的差量切片之間是沒有任何隔離機(jī)制的。
    3. 差量具有結(jié)構(gòu):虛擬機(jī)雖然支持增量備份,但是人們卻沒有合適的手段去主動構(gòu)造一個(gè)指定的差量切片出來。歸根結(jié)底,是因?yàn)樘摂M機(jī)的差量定義在二進(jìn)制字節(jié)空間中,而這個(gè)空間非常貧瘠,幾乎沒有什么用戶可以控制的構(gòu)造模式。而Docker的差量是定義在差量文件系統(tǒng)空間中,這個(gè)空間繼承了Linux社區(qū)最豐富的歷史資源。每一條shell指令的執(zhí)行結(jié)果最終反映到文件系統(tǒng)中都是增加/刪除/修改了某些文件,所以每一條shell指令都可以被看作是某個(gè)差量的定義。差量構(gòu)成了一個(gè)異常豐富的結(jié)構(gòu)空間,差量既是這個(gè)空間中的變換算符(shell指令),又是變換算符的運(yùn)算結(jié)果。差量與差量相遇產(chǎn)生新的差量,這種生生不息才是Docker的生命力所在。

    (二)React

    2013年,也就是Docker發(fā)布的同一年,F(xiàn)acebook公司開源了一個(gè)革命性的Web前端框架React。React的技術(shù)思想非常獨(dú)特,它以函數(shù)式編程思想為基礎(chǔ),結(jié)合一個(gè)看似異想天開的虛擬DOM(Virtual DOM)概念,引入了一整套新的設(shè)計(jì)模式,開啟了前端開發(fā)的新大航海時(shí)代。

    class HelloMessage extends React.Component {
      constructor(props) {
        super(props);
        
    this.state = { count: 0 };
        
    this.action = this.action.bind(this);
      }
      
      action(){
        
    this.setState(state => ({
          count: state.count 
    + 1
        }));
      },
      
      render() {
        
    return (
          
    <button onClick={this.action}>
            Hello {
    this.props.name}:{this.state.count}
          
    </button>
        );
      }
    }

    ReactDOM.render(
      
    <HelloMessage name="Taylor" />,
      mountNode
    );

    React組件的核心是render函數(shù),它的設(shè)計(jì)參考了后端常見的模板渲染技術(shù),主要區(qū)別在于后端模板輸出的是HTML文本,而React組件的Render函數(shù)使用類似XML模板的JSX語法,通過編譯轉(zhuǎn)換在運(yùn)行時(shí)輸出的是虛擬DOM節(jié)點(diǎn)對象。例如上面HelloMessage組件的render函數(shù)被翻譯后的結(jié)果類似于

    render(){
       
    return new VNode("button", {onClick: this.action, 
              content: 
    "Hello "+ this.props.name + ":" + this.state.count });
    }
    可以用以下公式來描述React組件:  VDom = render(state)
    當(dāng)狀態(tài)發(fā)生變化以后,只要重新執(zhí)行render函數(shù)就會生成新的虛擬DOM節(jié)點(diǎn),虛擬DOM節(jié)點(diǎn)可以被翻譯成真實(shí)的HTML DOM對象,從而實(shí)現(xiàn)界面更新。這種根據(jù)狀態(tài)重新生成完整視圖的渲染策略極大簡化了前端界面開發(fā)。例如對于一個(gè)列表界面,傳統(tǒng)編程需要編寫新增行/更新行/刪除行等多個(gè)不同的DOM操作函數(shù),而在React中只要更改state后重新執(zhí)行唯一的render函數(shù)即可。

    每次重新生成DOM視圖的唯一問題是性能很低,特別是當(dāng)前端交互操作眾多、狀態(tài)變化頻繁的時(shí)候。React的神來之筆是提出了基于虛擬DOM的diff算法,可以自動的計(jì)算兩個(gè)虛擬DOM樹之間的差量,狀態(tài)變化時(shí)只要執(zhí)行虛擬Dom差量對應(yīng)的DOM修改操作即可(更新真實(shí)DOM時(shí)會觸發(fā)樣式計(jì)算和布局計(jì)算,導(dǎo)致性能很低,而在JavaScript中操作虛擬DOM 的速度是非常快的)。整體策略可以表示為如下公式

    state_1=state_0\oplus action\\ \Delta VDom=render(state_1)-render(state_0)\\ \Delta Dom =Translater(\Delta VDom) \\

    顯然,這一策略也是可逆計(jì)算的一種特例。

    只要稍微留意一下就會發(fā)現(xiàn),最近幾年merge/diff/residual/delta等表達(dá)差量運(yùn)算的概念越來越多的出現(xiàn)在軟件設(shè)計(jì)領(lǐng)域中。比如大數(shù)據(jù)領(lǐng)域的流計(jì)算引擎中,流與表之間的關(guān)系可以表示為

    Table=\int \Delta Stream \\

    對表的增刪改查操作可以被編碼為事件流,而將表示數(shù)據(jù)變化的事件累積到一起就形成了數(shù)據(jù)表。

    現(xiàn)代科學(xué)發(fā)端于微積分的發(fā)明,而微分的本質(zhì)就是自動計(jì)算無窮小差量,而積分則是微分的逆運(yùn)算,自動對無窮小量進(jìn)行匯總合并。19世紀(jì)70年代,經(jīng)濟(jì)學(xué)經(jīng)歷了一場邊際革命,將微積分的思想引入經(jīng)濟(jì)分析,在邊際這一概念之上重建了整個(gè)經(jīng)濟(jì)學(xué)大廈。軟件構(gòu)造理論發(fā)展到今天,已經(jīng)進(jìn)入一個(gè)瓶頸,也到了應(yīng)該重新認(rèn)識差量的時(shí)候。

    四. 結(jié)語

    筆者的專業(yè)背景是理論物理學(xué),可逆計(jì)算源于筆者將物理學(xué)和數(shù)學(xué)的思想引入軟件領(lǐng)域的一種嘗試,它最早由筆者在2007年左右提出。一直以來,軟件領(lǐng)域?qū)τ谧匀灰?guī)律的應(yīng)用一般情況下都只限于"模擬"范疇,例如流體動力學(xué)模擬軟件,雖然它內(nèi)置了人類所認(rèn)知的最深刻的一些世界規(guī)律,但這些規(guī)律并沒有被用于指導(dǎo)和定義軟件世界自身的構(gòu)造和演化,它們的指向范圍是軟件世界之外,而不是軟件世界自身。在筆者看來,在軟件世界中,我們完全可以站在“上帝的視角”,規(guī)劃和定義一系列的結(jié)構(gòu)構(gòu)造規(guī)律,輔助我們完成軟件世界的構(gòu)建。而為了完成這一點(diǎn),我們首先需要建立程序世界中的“微積分”。

    類似于微積分,可逆計(jì)算理論的核心是將“差量”提升為第一性的概念,將全量看作是差量的一種特例(全量=單位元+全量)。傳統(tǒng)的程序世界中我們所表達(dá)的都只是“有”,而且是“所有”,差量只能通過全量之間的運(yùn)算間接得到,它的表述和操縱都需要特殊處理,而基于可逆計(jì)算理論,我們首先應(yīng)該定義所有差量概念的表達(dá)形式,然后再圍繞這些概念去建立整個(gè)領(lǐng)域概念體系。為了保證差量所在數(shù)學(xué)空間的完備性(差量之間的運(yùn)算結(jié)果仍然需要是合法的差量),差量所表達(dá)的不能僅僅是“有”,而必須是“有”和“沒有”的一種混合體。也就是說差量必須是“可逆的”。可逆性具有非常深刻的物理學(xué)內(nèi)涵,在基本的概念體系中內(nèi)置這一概念可以解決很多非常棘手的軟件構(gòu)造問題。

    為了處理分布式問題,現(xiàn)代軟件開發(fā)體系已經(jīng)接受了不可變數(shù)據(jù)的概念,而為了解決大粒度軟件復(fù)用問題,我們還需要接受不可變邏輯的概念(復(fù)用可以看作是保持原有邏輯不變,然后增加差量描述)。目前,業(yè)內(nèi)已經(jīng)逐步出現(xiàn)了一些富有創(chuàng)造性的主動應(yīng)用差量概念的實(shí)踐,它們都可以在可逆計(jì)算的理論框架下得到統(tǒng)一的詮釋。筆者提出了一種新的程序語言X語言,它可以極大簡化可逆計(jì)算的技術(shù)實(shí)現(xiàn)。目前筆者已經(jīng)基于X語言設(shè)計(jì)并實(shí)現(xiàn)了一系列軟件框架和生產(chǎn)工具,并基于它們提出了一種新的軟件生產(chǎn)范式(NOP)。

    posted @ 2019-04-29 10:18 canonical 閱讀(725) | 評論 (0)編輯 收藏

      瀏覽器前端編程的面貌自2005年以來已經(jīng)發(fā)生了深刻的變化,這并不簡單的意味著出現(xiàn)了大量功能豐富的基礎(chǔ)庫,使得我們可以更加方便的編寫業(yè)務(wù)代碼,更重要的是我們看待前端技術(shù)的觀念發(fā)生了重大轉(zhuǎn)變,明確意識到了如何以前端特有的方式釋放程序員的生產(chǎn)力。本文將結(jié)合jQuery源碼的實(shí)現(xiàn)原理,對javascript中涌現(xiàn)出的編程范式和常用技巧作一簡單介紹。
     
    1. AJAX: 狀態(tài)駐留,異步更新
      首先來看一點(diǎn)歷史。
    A. 1995年Netscape公司的Brendan Eich開發(fā)了javacript語言,這是一種動態(tài)(dynamic)、弱類型(weakly typed)、基于原型(prototype-based)的腳本語言。
    B. 1999年微軟IE5發(fā)布,其中包含了XMLHTTP ActiveX控件。
    C. 2001年微軟IE6發(fā)布,部分支持DOM level 1和CSS 2標(biāo)準(zhǔn)。
    D. 2002年Douglas Crockford發(fā)明JSON格式。
    至此,可以說Web2.0所依賴的技術(shù)元素已經(jīng)基本成形,但是并沒有立刻在整個(gè)業(yè)界產(chǎn)生重大的影響。盡管一些“頁面異步局部刷新”的技巧在程序員中間秘密的流傳,甚至催生了bindows這樣龐大臃腫的類庫,但總的來說,前端被看作是貧瘠而又骯臟的沼澤地,只有后臺技術(shù)才是王道。到底還缺少些什么呢?
      當(dāng)我們站在今天的角度去回顧2005年之前的js代碼,包括那些當(dāng)時(shí)的牛人所寫的代碼,可以明顯的感受到它們在程序控制力上的孱弱。并不是說2005年之前的js技術(shù)本身存在問題,只是它們在概念層面上是一盤散沙,缺乏統(tǒng)一的觀念,或者說缺少自己獨(dú)特的風(fēng)格, 自己的靈魂。當(dāng)時(shí)大多數(shù)的人,大多數(shù)的技術(shù)都試圖在模擬傳統(tǒng)的面向?qū)ο笳Z言,利用傳統(tǒng)的面向?qū)ο蠹夹g(shù),去實(shí)現(xiàn)傳統(tǒng)的GUI模型的仿制品。
      2005年是變革的一年,也是創(chuàng)造概念的一年。伴隨著Google一系列讓人耳目一新的交互式應(yīng)用的發(fā)布,Jesse James Garrett的一篇文章《Ajax: A New Approach to Web Applications》被廣為傳播。Ajax這一前端特有的概念迅速將眾多分散的實(shí)踐統(tǒng)一在同一口號之下,引發(fā)了Web編程范式的轉(zhuǎn)換。所謂名不正則言不順,這下無名群眾可找到組織了。在未有Ajax之前,人們早已認(rèn)識到了B/S架構(gòu)的本質(zhì)特征在于瀏覽器和服務(wù)器的狀態(tài)空間是分離的,但是一般的解決方案都是隱藏這一區(qū)分,將前臺狀態(tài)同步到后臺,由后臺統(tǒng)一進(jìn)行邏輯處理,例如ASP.NET。因?yàn)槿狈Τ墒斓脑O(shè)計(jì)模式支持前臺狀態(tài)駐留,在換頁的時(shí)候,已經(jīng)裝載的js對象將被迫被丟棄,這樣誰還能指望它去完成什么復(fù)雜的工作嗎?
      Ajax明確提出界面是局部刷新的,前臺駐留了狀態(tài),這就促成了一種需要:需要js對象在前臺存在更長的時(shí)間。這也就意味著需要將這些對象和功能有效的管理起來,意味著更復(fù)雜的代碼組織技術(shù),意味著對模塊化,對公共代碼基的渴求。
      jQuery現(xiàn)有的代碼中真正與Ajax相關(guān)(使用XMLHTTP控件異步訪問后臺返回?cái)?shù)據(jù))的部分其實(shí)很少,但是如果沒有Ajax, jQuery作為公共代碼基也就缺乏存在的理由。

    2. 模塊化:管理名字空間
      當(dāng)大量的代碼產(chǎn)生出來以后,我們所需要的最基礎(chǔ)的概念就是模塊化,也就是對工作進(jìn)行分解和復(fù)用。工作得以分解的關(guān)鍵在于各人獨(dú)立工作的成果可以集成在一起。這意味著各個(gè)模塊必須基于一致的底層概念,可以實(shí)現(xiàn)交互,也就是說應(yīng)該基于一套公共代碼基,屏蔽底層瀏覽器的不一致性,并實(shí)現(xiàn)統(tǒng)一的抽象層,例如統(tǒng)一的事件管理機(jī)制等。比統(tǒng)一代碼基更重要的是,各個(gè)模塊之間必須沒有名字沖突。否則,即使兩個(gè)模塊之間沒有任何交互,也無法共同工作。
      jQuery目前鼓吹的主要賣點(diǎn)之一就是對名字空間的良好控制。這甚至比提供更多更完善的功能點(diǎn)都重要的多。良好的模塊化允許我們復(fù)用任何來源的代碼,所有人的工作得以積累疊加。而功能實(shí)現(xiàn)僅僅是一時(shí)的工作量的問題。jQuery使用module pattern的一個(gè)變種來減少對全局名字空間的影響,僅僅在window對象上增加了一個(gè)jQuery對象(也就是$函數(shù))。
       所謂的module pattern代碼如下,它的關(guān)鍵是利用匿名函數(shù)限制臨時(shí)變量的作用域。
      var feature =(function() {

    // 私有變量和函數(shù)
    var privateThing = 'secret',
        publicThing = 'not secret',

        changePrivateThing = function() {
            privateThing = 'super secret';
        },

        sayPrivateThing = function() {
            console.log(privateThing);
            changePrivateThing();
        };

    // 返回對外公開的API
    return {
        publicThing : publicThing,
        sayPrivateThing :  sayPrivateThing
    }
    })();
      js本身缺乏包結(jié)構(gòu),不過經(jīng)過多年的嘗試之后業(yè)內(nèi)已經(jīng)逐漸統(tǒng)一了對包加載的認(rèn)識,形成了RequireJs庫這樣得到一定共識的解決方案。jQuery可以與RequireJS庫良好的集成在一起, 實(shí)現(xiàn)更完善的模塊依賴管理。http://requirejs.org/docs/jquery.html
     
      require(["jquery", "jquery.my"], function() {
        //當(dāng)jquery.js和jquery.my.js都成功裝載之后執(zhí)行
        $(function(){
          $('#my').myFunc();
        });
      });
     
      通過以下函數(shù)調(diào)用來定義模塊my/shirt, 它依賴于my/cart和my/inventory模塊,
      require.def("my/shirt",
        ["my/cart", "my/inventory"],
        function(cart, inventory) {
            // 這里使用module pattern來返回my/shirt模塊對外暴露的API
            return {
                color: "blue",
                size: "large"
                addToCart: function() {
                    // decrement是my/inventory對外暴露的API
                    inventory.decrement(this);
                    cart.add(this);
                }
            }
        }
      );

    3. 神奇的$:對象提升
      當(dāng)你第一眼看到$函數(shù)的時(shí)候,你想到了什么?傳統(tǒng)的編程理論總是告訴我們函數(shù)命名應(yīng)該準(zhǔn)確,應(yīng)該清晰無誤的表達(dá)作者的意圖,甚至聲稱長名字要優(yōu)于短名字,因?yàn)闇p少了出現(xiàn)歧義的可能性。但是,$是什么?亂碼?它所傳遞的信息實(shí)在是太隱晦,太曖昧了。$是由prototype.js庫發(fā)明的,它真的是一個(gè)神奇的函數(shù),因?yàn)樗梢詫⒁粋€(gè)原始的DOM節(jié)點(diǎn)提升(enhance)為一個(gè)具有復(fù)雜行為的對象。在prototype.js最初的實(shí)現(xiàn)中,$函數(shù)的定義為
      var $ = function (id) {
        return "string" == typeof id ? document.getElementById(id) : id;
      };
      這基本對應(yīng)于如下公式
          e = $(id)
      這絕不僅僅是提供了一個(gè)聰明的函數(shù)名稱縮寫,更重要的是在概念層面上建立了文本id與DOM element之間的一一對應(yīng)。在未有$之前,id與對應(yīng)的element之間的距離十分遙遠(yuǎn),一般要將element緩存到變量中,例如
      var ea = docuement.getElementById('a');
      var eb = docuement.getElementById('b');
      ea.style....
    但是使用$之后,卻隨處可見如下的寫法
      $('header_'+id).style...
      $('body_'+id)....
    id與element之間的距離似乎被消除了,可以非常緊密的交織在一起。
      prototype.js后來擴(kuò)展了$的含義,
      function $() {
        var elements = new Array();
        
        for (var i = 0; i < arguments.length; i++) {
            var element = arguments[i];
            if (typeof element == 'string')
              element = document.getElementById(element);
        
            if (arguments.length == 1)
              return element;
        
            elements.push(element);
        }
        
        return elements;
      }
      這對應(yīng)于公式
        [e,e] = $(id,id)
      很遺憾,這一步prototype.js走偏了,這一做法很少有實(shí)用的價(jià)值。
      真正將$發(fā)揚(yáng)光大的是jQuery, 它的$對應(yīng)于公式
        [o] = $(selector)
      這里有三個(gè)增強(qiáng)
      A. selector不再是單一的節(jié)點(diǎn)定位符,而是復(fù)雜的集合選擇符
      B. 返回的元素不是原始的DOM節(jié)點(diǎn),而是經(jīng)過jQuery進(jìn)一步增強(qiáng)的具有豐富行為的對象,可以啟動復(fù)雜的函數(shù)調(diào)用鏈。
      C. $返回的包裝對象被造型為數(shù)組形式,將集合操作自然的整合到調(diào)用鏈中。

      當(dāng)然,以上僅僅是對神奇的$的一個(gè)過分簡化的描述,它的實(shí)際功能要復(fù)雜得多. 特別是有一個(gè)非常常用的直接構(gòu)造功能.
       $("<table><tbody><tr><td>...</td></tr></tbody></table>")....
      jQuery將根據(jù)傳入的html文本直接構(gòu)造出一系列的DOM節(jié)點(diǎn),并將其包裝為jQuery對象. 這在某種程度上可以看作是對selector的擴(kuò)展: html內(nèi)容描述本身就是一種唯一指定.
      $(function{})這一功能就實(shí)在是讓人有些無語了, 它表示當(dāng)document.ready的時(shí)候調(diào)用此回調(diào)函數(shù)。真的,$是一個(gè)神奇的函數(shù), 有任何問題,請$一下。
      總結(jié)起來, $是從普通的DOM和文本描述世界到具有豐富對象行為的jQuery世界的躍遷通道。跨過了這道門,就來到了理想國。
       
    4. 無定形的參數(shù):專注表達(dá)而不是約束
      弱類型語言既然頭上頂著個(gè)"弱"字, 總難免讓人有些先天不足的感覺. 在程序中缺乏類型約束, 是否真的是一種重大的缺憾? 在傳統(tǒng)的強(qiáng)類型語言中, 函數(shù)參數(shù)的類型,個(gè)數(shù)等都是由編譯器負(fù)責(zé)檢查的約束條件, 但這些約束仍然是遠(yuǎn)遠(yuǎn)不夠的. 一般應(yīng)用程序中為了加強(qiáng)約束, 總會增加大量防御性代碼, 例如在C++中我們常用ASSERT, 而在java中也經(jīng)常需要判斷參數(shù)值的范圍
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException(
                "Index: "+index+", Size: "+size);            
      很顯然, 這些代碼將導(dǎo)致程序中存在大量無功能的執(zhí)行路徑, 即我們做了大量判斷, 代碼執(zhí)行到某個(gè)點(diǎn), 系統(tǒng)拋出異常, 大喊此路不通. 如果我們換一個(gè)思路, 既然已經(jīng)做了某種判斷,能否利用這些判斷的結(jié)果來做些什么呢? javascript是一種弱類型的語言,它是無法自動約束參數(shù)類型的, 那如果順勢而行,進(jìn)一步弱化參數(shù)的形態(tài), 將"弱"推進(jìn)到一種極致, 在弱無可弱的時(shí)候, weak會不會成為標(biāo)志性的特點(diǎn)?
      看一下jQuery中的事件綁定函數(shù)bind,
       A. 一次綁定一個(gè)事件 $("#my").bind("mouseover", function(){});
       B. 一次綁定多個(gè)事件 $("#my").bind("mouseover mouseout",function(){})
       C. 換一個(gè)形式, 同樣綁定多個(gè)事件
          $("#my").bind({mouseover:function(){}, mouseout:function(){});
       D. 想給事件監(jiān)聽器傳點(diǎn)參數(shù)
          $('#my').bind('click', {foo: "xxxx"}, function(event) { event.data.foo..})
       E. 想給事件監(jiān)聽器分個(gè)組
          $("#my").bind("click.myGroup″, function(){});
       F. 這個(gè)函數(shù)為什么還沒有瘋掉???
       
       就算是類型不確定, 在固定位置上的參數(shù)的意義總要是確定的吧? 退一萬步來說, 就算是參數(shù)位置不重要了,函數(shù)本身的意義應(yīng)該是確定的吧? 但這是什么?
          取值 value = o.val(), 設(shè)置值 o.val(3)
          
       一個(gè)函數(shù)怎么可以這樣過分, 怎么能根據(jù)傳入?yún)?shù)的類型和個(gè)數(shù)不同而行為不同呢? 看不順眼是不是? 可這就是俺們的價(jià)值觀. 既然不能防止, 那就故意允許. 雖然形式多變, 卻無一句廢話. 缺少約束, 不妨礙表達(dá)(我不是出來嚇人的).
       
    5. 鏈?zhǔn)讲僮? 線性化的逐步細(xì)化
      jQuery早期最主要的賣點(diǎn)就是所謂的鏈?zhǔn)讲僮?chain).
     
      $('#content') // 找到content元素
        .find('h3') // 選擇所有后代h3節(jié)點(diǎn)
        .eq(2)      // 過濾集合, 保留第三個(gè)元素
            .html('改變第三個(gè)h3的文本')
        .end()      // 返回上一級的h3集合
        .eq(0)
            .html('改變第一個(gè)h3的文本');

    在一般的命令式語言中, 我們總需要在重重嵌套循環(huán)中過濾數(shù)據(jù), 實(shí)際操作數(shù)據(jù)的代碼與定位數(shù)據(jù)的代碼糾纏在一起. 而jQuery采用先構(gòu)造集合然后再應(yīng)用函數(shù)于集合的方式實(shí)現(xiàn)兩種邏輯的解耦, 實(shí)現(xiàn)嵌套結(jié)構(gòu)的線性化. 實(shí)際上, 我們并不需要借助過程化的思想就可以很直觀的理解一個(gè)集合, 例如 $('div.my input:checked')可以看作是一種直接的描述,而不是對過程行為的跟蹤.
       循環(huán)意味著我們的思維處于一種反復(fù)回繞的狀態(tài), 而線性化之后則沿著一個(gè)方向直線前進(jìn), 極大減輕了思維負(fù)擔(dān), 提高了代碼的可組合性. 為了減少調(diào)用鏈的中斷, jQuery發(fā)明了一個(gè)絕妙的主意: jQuery包裝對象本身類似數(shù)組(集合). 集合可以映射到新的集合, 集合可以限制到自己的子集合,調(diào)用的發(fā)起者是集合,返回結(jié)果也是集合,集合可以發(fā)生結(jié)構(gòu)上的某種變化但它還是集合, 集合是某種概念上的不動點(diǎn),這是從函數(shù)式語言中吸取的設(shè)計(jì)思想。集合操作是太常見的操作, 在java中我們很容易發(fā)現(xiàn)大量所謂的封裝函數(shù)其實(shí)就是在封裝一些集合遍歷操作, 而在jQuery中集合操作因?yàn)樘卑锥恍枰庋b.
       鏈?zhǔn)秸{(diào)用意味著我們始終擁有一個(gè)“當(dāng)前”對象,所有的操作都是針對這一當(dāng)前對象進(jìn)行。這對應(yīng)于如下公式
         x += dx
    調(diào)用鏈的每一步都是對當(dāng)前對象的增量描述,是針對最終目標(biāo)的逐步細(xì)化過程。Witrix平臺中對這一思想也有著廣泛的應(yīng)用。特別是為了實(shí)現(xiàn)平臺機(jī)制與業(yè)務(wù)代碼的融合,平臺會提供對象(容器)的缺省內(nèi)容,而業(yè)務(wù)代碼可以在此基礎(chǔ)上進(jìn)行逐步細(xì)化的修正,包括取消缺省的設(shè)置等。
      話說回來, 雖然表面上jQuery的鏈?zhǔn)秸{(diào)用很簡單, 內(nèi)部實(shí)現(xiàn)的時(shí)候卻必須自己多寫一層循環(huán), 因?yàn)榫幾g器并不知道"自動應(yīng)用于集合中每個(gè)元素"這回事.
      $.fn['someFunc'] = function(){
        return this.each(function(){
          jQuery.someFunc(this,...);
        }
      }
     
    6. data: 統(tǒng)一數(shù)據(jù)管理
      作為一個(gè)js庫,它必須解決的一個(gè)大問題就是js對象與DOM節(jié)點(diǎn)之間的狀態(tài)關(guān)聯(lián)與協(xié)同管理問題。有些js庫選擇以js對象為主,在js對象的成員變量中保存DOM節(jié)點(diǎn)指針,訪問時(shí)總是以js對象為入口點(diǎn),通過js函數(shù)間接操作DOM對象。在這種封裝下,DOM節(jié)點(diǎn)其實(shí)只是作為界面展現(xiàn)的一種底層“匯編”而已。jQuery的選擇與Witrix平臺類似,都是以HTML自身結(jié)構(gòu)為基礎(chǔ),通過js增強(qiáng)(enhance)DOM節(jié)點(diǎn)的功能,將它提升為一個(gè)具有復(fù)雜行為的擴(kuò)展對象。這里的思想是非侵入式設(shè)計(jì)(non-intrusive)和優(yōu)雅退化機(jī)制(graceful degradation)。語義結(jié)構(gòu)在基礎(chǔ)的HTML層面是完整的,js的作用是增強(qiáng)了交互行為,控制了展現(xiàn)形式。
      如果每次我們都通過$('#my')的方式來訪問相應(yīng)的包裝對象,那么一些需要長期保持的狀態(tài)變量保存在什么地方呢?jQuery提供了一個(gè)統(tǒng)一的全局?jǐn)?shù)據(jù)管理機(jī)制。
      獲取數(shù)據(jù) $('#my').data('myAttr')   設(shè)置數(shù)據(jù) $('#my').data('myAttr',3);
    這一機(jī)制自然融合了對HTML5的data屬性的處理
       <input id="my" data-my-attr="4" ... />
     通過 $('#my').data('myAttr')將可以讀取到HTML中設(shè)置的數(shù)據(jù)。
     
     第一次訪問data時(shí),jQuery將為DOM節(jié)點(diǎn)分配一個(gè)唯一的uuid, 然后設(shè)置在DOM節(jié)點(diǎn)的一個(gè)特定的expando屬性上, jQuery保證這個(gè)uuid在本頁面中不重復(fù)。
       elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
     以上代碼可以同時(shí)處理DOM節(jié)點(diǎn)和純js對象的情況。如果是js對象,則data直接放置在js對象自身中,而如果是DOM節(jié)點(diǎn),則通過cache統(tǒng)一管理。
     因?yàn)樗械臄?shù)據(jù)都是通過data機(jī)制統(tǒng)一管理的,特別是包括所有事件監(jiān)聽函數(shù)(data.events),因此jQuery可以安全的實(shí)現(xiàn)資源管理。在clone節(jié)點(diǎn)的時(shí)候,可以自動clone其相關(guān)的事件監(jiān)聽函數(shù)。而當(dāng)DOM節(jié)點(diǎn)的內(nèi)容被替換或者DOM節(jié)點(diǎn)被銷毀的時(shí)候,jQuery也可以自動解除事件監(jiān)聽函數(shù), 并安全的釋放相關(guān)的js數(shù)據(jù)。
     
    7. event:統(tǒng)一事件模型
      "事件沿著對象樹傳播"這一圖景是面向?qū)ο蠼缑婢幊棠P偷木杷凇ο蟮膹?fù)合構(gòu)成對界面結(jié)構(gòu)的一個(gè)穩(wěn)定的描述,事件不斷在對象樹的某個(gè)節(jié)點(diǎn)發(fā)生,并通過冒泡機(jī)制向上傳播。對象樹很自然的成為一個(gè)控制結(jié)構(gòu),我們可以在父節(jié)點(diǎn)上監(jiān)聽所有子節(jié)點(diǎn)上的事件,而不用明確與每一個(gè)子節(jié)點(diǎn)建立關(guān)聯(lián)。
      jQuery除了為不同瀏覽器的事件模型建立了統(tǒng)一抽象之外,主要做了如下增強(qiáng):
      A. 增加了自定制事件(custom)機(jī)制. 事件的傳播機(jī)制與事件內(nèi)容本身原則上是無關(guān)的, 因此自定制事件完全可以和瀏覽器內(nèi)置事件通過同一條處理路徑, 采用同樣的監(jiān)聽方式. 使用自定制事件可以增強(qiáng)代碼的內(nèi)聚性, 減少代碼耦合. 例如如果沒有自定制事件, 關(guān)聯(lián)代碼往往需要直接操作相關(guān)的對象
      $('.switch, .clapper').click(function() {
        var $light = $(this).parent().find('.lightbulb');
        if ($light.hasClass('on')) {
            $light.removeClass('on').addClass('off');
        } else {
            $light.removeClass('off').addClass('on');
        }
      });
    而如果使用自定制事件,則表達(dá)的語義更加內(nèi)斂明確,
      $('.switch, .clapper').click(function() {
        $(this).parent().find('.lightbulb').trigger('changeState');
      });
      B. 增加了對動態(tài)創(chuàng)建節(jié)點(diǎn)的事件監(jiān)聽. bind函數(shù)只能將監(jiān)聽函數(shù)注冊到已經(jīng)存在的DOM節(jié)點(diǎn)上. 例如
        $('li.trigger').bind('click',function(){}}
      如果調(diào)用bind之后,新建了另一個(gè)li節(jié)點(diǎn),則該節(jié)點(diǎn)的click事件不會被監(jiān)聽.
      jQuery的delegate機(jī)制可以將監(jiān)聽函數(shù)注冊到父節(jié)點(diǎn)上, 子節(jié)點(diǎn)上觸發(fā)的事件會根據(jù)selector被自動派發(fā)到相應(yīng)的handlerFn上. 這樣一來現(xiàn)在注冊就可以監(jiān)聽未來創(chuàng)建的節(jié)點(diǎn).
        $('#myList').delegate('li.trigger', 'click', handlerFn);
      最近jQuery1.7中統(tǒng)一了bind, live和delegate機(jī)制, 天下一統(tǒng), 只有on/off.
        $('li.trigger’).on('click', handlerFn);  // 相當(dāng)于bind
        $('#myList’).on('click', 'li.trigger', handlerFn);  // 相當(dāng)于delegate
        
    8. 動畫隊(duì)列:全局時(shí)鐘協(xié)調(diào)
      拋開jQuery的實(shí)現(xiàn)不談, 先考慮一下如果我們要實(shí)現(xiàn)界面上的動畫效果, 到底需要做些什么? 比如我們希望將一個(gè)div的寬度在1秒鐘之內(nèi)從100px增加到200px. 很容易想見, 在一段時(shí)間內(nèi)我們需要不時(shí)的去調(diào)整一下div的寬度, [同時(shí)]我們還需要執(zhí)行其他代碼. 與一般的函數(shù)調(diào)用不同的是, 發(fā)出動畫指令之后, 我們不能期待立刻得到想要的結(jié)果, 而且我們不能原地等待結(jié)果的到來. 動畫的復(fù)雜性就在于:一次性表達(dá)之后要在一段時(shí)間內(nèi)執(zhí)行,而且有多條邏輯上的執(zhí)行路徑要同時(shí)展開, 如何協(xié)調(diào)?
      偉大的艾薩克.牛頓爵士在《自然哲學(xué)的數(shù)學(xué)原理》中寫道:"絕對的、真正的和數(shù)學(xué)的時(shí)間自身在流逝著". 所有的事件可以在時(shí)間軸上對齊, 這就是它們內(nèi)在的協(xié)調(diào)性. 因此為了從步驟A1執(zhí)行到A5, 同時(shí)將步驟B1執(zhí)行到B5, 我們只需要在t1時(shí)刻執(zhí)行[A1, B1], 在t2時(shí)刻執(zhí)行[A2,B2], 依此類推.
        t1 | t2 | t3 | t4 | t5 ...
        A1 | A2 | A3 | A4 | A5 ...
        B1 | B2 | B3 | B4 | B5 ...
      具體的一種實(shí)現(xiàn)形式可以是
      A. 對每個(gè)動畫, 將其分裝為一個(gè)Animation對象, 內(nèi)部分成多個(gè)步驟.
          animation = new Animation(div,"width",100,200,1000,
                      負(fù)責(zé)步驟切分的插值函數(shù),動畫執(zhí)行完畢時(shí)的回調(diào)函數(shù));
      B. 在全局管理器中注冊動畫對象
          timerFuncs.add(animation);
      C. 在全局時(shí)鐘的每一個(gè)觸發(fā)時(shí)刻, 將每個(gè)注冊的執(zhí)行序列推進(jìn)一步, 如果已經(jīng)結(jié)束, 則從全局管理器中刪除.
         for each animation in timerFuncs
            if(!animation.doOneStep())
               timerFuncs.remove(animation)

      解決了原理問題,再來看看表達(dá)問題, 怎樣設(shè)計(jì)接口函數(shù)才能夠以最緊湊形式表達(dá)我們的意圖? 我們經(jīng)常需要面臨的實(shí)際問題:
      A. 有多個(gè)元素要執(zhí)行類似的動畫
      B. 每個(gè)元素有多個(gè)屬性要同時(shí)變化
      C. 執(zhí)行完一個(gè)動畫之后開始另一個(gè)動畫
    jQuery對這些問題的解答可以說是榨盡了js語法表達(dá)力的最后一點(diǎn)剩余價(jià)值.
       $('input')
         .animate({left:'+=200px',top:'300'},2000)
         .animate({left:'-=200px',top:20},1000)
         .queue(function(){
           // 這里dequeue將首先執(zhí)行隊(duì)列中的后一個(gè)函數(shù),因此alert("y")
           $(this).dequeue();
           alert('x');
          })
         .queue(function(){
            alert("y");
            // 如果不主動dequeue, 隊(duì)列執(zhí)行就中斷了,不會自動繼續(xù)下去.
            $(this).dequeue();
          });

      A. 利用jQuery內(nèi)置的selector機(jī)制自然表達(dá)對一個(gè)集合的處理.
      B. 使用Map表達(dá)多個(gè)屬性變化
      C. 利用微格式表達(dá)領(lǐng)域特定的差量概念. '+=200px'表示在現(xiàn)有值的基礎(chǔ)上增加200px
      D. 利用函數(shù)調(diào)用的順序自動定義animation執(zhí)行的順序: 在后面追加到執(zhí)行隊(duì)列中的動畫自然要等前面的動畫完全執(zhí)行完畢之后再啟動.
       
      jQuery動畫隊(duì)列的實(shí)現(xiàn)細(xì)節(jié)大概如下所示,
       A. animate函數(shù)實(shí)際是調(diào)用queue(function(){執(zhí)行結(jié)束時(shí)需要調(diào)用dequeue,否則不會驅(qū)動下一個(gè)方法})
          queue函數(shù)執(zhí)行時(shí), 如果是fx隊(duì)列, 并且當(dāng)前沒有正在運(yùn)行動畫(如果連續(xù)調(diào)用兩次animate,第二次的執(zhí)行函數(shù)將在隊(duì)列中等待),則會自動觸發(fā)dequeue操作, 驅(qū)動隊(duì)列運(yùn)行.
          如果是fx隊(duì)列, dequeue的時(shí)候會自動在隊(duì)列頂端加入"inprogress"字符串,表示將要執(zhí)行的是動畫.
       B. 針對每一個(gè)屬性,創(chuàng)建一個(gè)jQuery.fx對象。然后調(diào)用fx.custom函數(shù)(相當(dāng)于start)來啟動動畫。
       C. custom函數(shù)中將fx.step函數(shù)注冊到全局的timerFuncs中,然后試圖啟動一個(gè)全局的timer.
           timerId = setInterval( fx.tick, fx.interval );
       D. 靜態(tài)的tick函數(shù)中將依次調(diào)用各個(gè)fx的step函數(shù)。step函數(shù)中通過easing計(jì)算屬性的當(dāng)前值,然后調(diào)用fx的update來更新屬性。
       E. fx的step函數(shù)中判斷如果所有屬性變化都已完成,則調(diào)用dequeue來驅(qū)動下一個(gè)方法。

      很有意思的是, jQuery的實(shí)現(xiàn)代碼中明顯有很多是接力觸發(fā)代碼: 如果需要執(zhí)行下一個(gè)動畫就取出執(zhí)行, 如果需要啟動timer就啟動timer等. 這是因?yàn)閖s程序是單線程的,真正的執(zhí)行路徑只有一條,為了保證執(zhí)行線索不中斷, 函數(shù)們不得不互相幫助一下. 可以想見, 如果程序內(nèi)部具有多個(gè)執(zhí)行引擎, 甚至無限多的執(zhí)行引擎, 那么程序的面貌就會發(fā)生本質(zhì)性的改變. 而在這種情形下, 遞歸相對于循環(huán)而言會成為更自然的描述.
     
    9. promise模式:因果關(guān)系的識別
      現(xiàn)實(shí)中,總有那么多時(shí)間線在獨(dú)立的演化著, 人與物在時(shí)空中交錯(cuò),卻沒有發(fā)生因果. 軟件中, 函數(shù)們在源代碼中排著隊(duì), 難免會產(chǎn)生一些疑問, 憑什么排在前面的要先執(zhí)行? 難道沒有它就沒有我? 讓全宇宙喊著1,2,3齊步前進(jìn), 從上帝的角度看,大概是管理難度過大了, 于是便有了相對論. 如果相互之間沒有交換信息, 沒有產(chǎn)生相互依賴, 那么在某個(gè)坐標(biāo)系中順序發(fā)生的事件, 在另外一個(gè)坐標(biāo)系中看來, 就可能是顛倒順序的. 程序員依葫蘆畫瓢, 便發(fā)明了promise模式.
      promise與future模式基本上是一回事,我們先來看一下java中熟悉的future模式.
      futureResult = doSomething();
      ...
      realResult = futureResult.get();
      發(fā)出函數(shù)調(diào)用僅僅意味著一件事情發(fā)生過, 并不必然意味著調(diào)用者需要了解事情最終的結(jié)果. 函數(shù)立刻返回的只是一個(gè)將在未來兌現(xiàn)的承諾(Future類型), 實(shí)際上也就是某種句柄. 句柄被傳來傳去, 中間轉(zhuǎn)手的代碼對實(shí)際結(jié)果是什么,是否已經(jīng)返回漠不關(guān)心. 直到一段代碼需要依賴調(diào)用返回的結(jié)果, 因此它打開future, 查看了一下. 如果實(shí)際結(jié)果已經(jīng)返回, 則future.get()立刻返回實(shí)際結(jié)果, 否則將會阻塞當(dāng)前的執(zhí)行路徑, 直到結(jié)果返回為止. 此后再調(diào)用future.get()總是立刻返回, 因?yàn)橐蚬P(guān)系已經(jīng)被建立, [結(jié)果返回]這一事件必然在此之前發(fā)生, 不會再發(fā)生變化.
      future模式一般是外部對象主動查看future的返回值, 而promise模式則是由外部對象在promise上注冊回調(diào)函數(shù).
      function getData(){
       return $.get('/foo/').done(function(){
          console.log('Fires after the AJAX request succeeds');
       }).fail(function(){
          console.log('Fires after the AJAX request fails');
       });
      }
     
      function showDiv(){
        var dfd = $.Deferred();
        $('#foo').fadeIn( 1000, dfd.resolve );
        return dfd.promise();
      }
     
      $.when( getData(), showDiv() )
        .then(function( ajaxResult, ignoreResultFromShowDiv ){
            console.log('Fires after BOTH showDiv() AND the AJAX request succeed!');
            // 'ajaxResult' is the server’s response
        });
      jQuery引入Deferred結(jié)構(gòu), 根據(jù)promise模式對ajax, queue, document.ready等進(jìn)行了重構(gòu), 統(tǒng)一了異步執(zhí)行機(jī)制. then(onDone, onFail)將向promise中追加回調(diào)函數(shù), 如果調(diào)用成功完成(resolve), 則回調(diào)函數(shù)onDone將被執(zhí)行, 而如果調(diào)用失敗(reject), 則onFail將被執(zhí)行. when可以等待在多個(gè)promise對象上. promise巧妙的地方是異步執(zhí)行已經(jīng)開始之后甚至已經(jīng)結(jié)束之后,仍然可以注冊回調(diào)函數(shù)
      someObj.done(callback).sendRequest() vs. someObj.sendRequest().done(callback)
     callback函數(shù)在發(fā)出異步調(diào)用之前注冊或者在發(fā)出異步調(diào)用之后注冊是完全等價(jià)的, 這揭示出程序表達(dá)永遠(yuǎn)不是完全精確的, 總存在著內(nèi)在的變化維度. 如果能有效利用這一內(nèi)在的可變性, 則可以極大提升并發(fā)程序的性能.
       promise模式的具體實(shí)現(xiàn)很簡單. jQuery._Deferred定義了一個(gè)函數(shù)隊(duì)列,它的作用有以下幾點(diǎn):
       A. 保存回調(diào)函數(shù)。
       B. 在resolve或者reject的時(shí)刻把保存著的函數(shù)全部執(zhí)行掉。
       C. 已經(jīng)執(zhí)行之后, 再增加的函數(shù)會被立刻執(zhí)行。
     
       一些專門面向分布式計(jì)算或者并行計(jì)算的語言會在語言級別內(nèi)置promise模式, 比如E語言.
         def carPromise := carMaker <- produce("Mercedes");
         def temperaturePromise := carPromise <- getEngineTemperature()
         ...
         when (temperaturePromise) -> done(temperature) {
           println(`The temperature of the car engine is: $temperature`)
         } catch e {
           println(`Could not get engine temperature, error: $e`)
         }
      在E語言中, <-是eventually運(yùn)算符, 表示最終會執(zhí)行, 但不一定是現(xiàn)在. 而普通的car.moveTo(2,3)表示立刻執(zhí)行得到結(jié)果. 編譯器負(fù)責(zé)識別所有的promise依賴, 并自動實(shí)現(xiàn)調(diào)度.
     
    10. extend: 繼承不是必須的
      js是基于原型的語言, 并沒有內(nèi)置的繼承機(jī)制, 這一直讓很多深受傳統(tǒng)面向?qū)ο蠼逃耐瑢W(xué)們耿耿于懷. 但繼承一定是必須的嗎? 它到底能夠給我們帶來什么? 最純樸的回答是: 代碼重用. 那么, 我們首先來分析一下繼承作為代碼重用手段的潛力.
      曾經(jīng)有個(gè)概念叫做"多重繼承", 它是繼承概念的超級賽亞人版, 很遺憾后來被診斷為存在著先天缺陷, 以致于出現(xiàn)了一種對于繼承概念的解讀: 繼承就是"is a"關(guān)系, 一個(gè)派生對象"is a"很多基類, 必然會出現(xiàn)精神分裂, 所以多重繼承是不好的.
       class A{ public: void f(){ f in A } }
       class B{ public: void f(){ f in B } }
       class D: public A, B{}
     如果D類從A,B兩個(gè)基類繼承, 而A和B類中都實(shí)現(xiàn)了同一個(gè)函數(shù)f, 那么D類中的f到底是A中的f還是B中的f, 抑或是A中的f+B中的f呢? 這一困境的出現(xiàn)實(shí)際上源于D的基類A和B是并列關(guān)系, 它們滿足交換律和結(jié)合律, 畢竟,在概念層面上我們可能難以認(rèn)可兩個(gè)任意概念之間會出現(xiàn)從屬關(guān)系. 但如果我們放松一些概念層面的要求, 更多的從操作層面考慮一下代碼重用問題, 可以簡單的認(rèn)為B在A的基礎(chǔ)上進(jìn)行操作, 那么就可以得到一個(gè)線性化的結(jié)果. 也就是說, 放棄A和B之間的交換律只保留結(jié)合律, extends A, B 與 extends B,A 會是兩個(gè)不同的結(jié)果, 不再存在詮釋上的二義性. scala語言中的所謂trait(特性)機(jī)制實(shí)際上采用的就是這一策略.
      面向?qū)ο蠹夹g(shù)發(fā)明很久之后, 出現(xiàn)了所謂的面向方面編程(AOP), 它與OOP不同, 是代碼結(jié)構(gòu)空間中的定位與修改技術(shù). AOP的眼中只有類與方法, 不知道什么叫做意義. AOP也提供了一種類似多重繼承的代碼重用手段, 那就是mixin. 對象被看作是可以被打開,然后任意修改的Map, 一組成員變量與方法就被直接注射到對象體內(nèi), 直接改變了它的行為.
      prototype.js庫引入了extend函數(shù),
      Object.extend = function(destination, source) {
        for (var property in source) {
          destination[property] = source[property];
        }
        return destination;
      }
      就是Map之間的一個(gè)覆蓋運(yùn)算, 但很管用, 在jQuery庫中也得到了延用. 這個(gè)操作類似于mixin, 在jQuery中是代碼重用的主要技術(shù)手段---沒有繼承也沒什么大不了的.

    11. 名稱映射: 一切都是數(shù)據(jù)
      代碼好不好, 循環(huán)判斷必須少. 循環(huán)和判斷語句是程序的基本組成部分, 但是優(yōu)良的代碼庫中卻往往找不到它們的蹤影, 因?yàn)檫@些語句的交織會模糊系統(tǒng)的邏輯主線, 使我們的思想迷失在疲于奔命的代碼追蹤中. jQuery本身通過each, extend等函數(shù)已經(jīng)極大減少了對循環(huán)語句的需求, 對于判斷語句, 則主要是通過映射表來處理. 例如, jQuery的val()函數(shù)需要針對不同標(biāo)簽進(jìn)行不同的處理, 因此定義一個(gè)以tagName為key的函數(shù)映射表
       valHooks: { option: {get:function(){}}}
    這樣在程序中就不需要到處寫
       if(elm.tagName == 'OPTION'){
         return ...;
       }else if(elm.tagName == 'TEXTAREA'){
         return ...;
       }
    可以統(tǒng)一處理
       (valHooks[elm.tagName.toLowerCase()] || defaultHandler).get(elm);
       
      映射表將函數(shù)作為普通數(shù)據(jù)來管理, 在動態(tài)語言中有著廣泛的應(yīng)用. 特別是, 對象本身就是函數(shù)和變量的容器, 可以被看作是映射表. jQuery中大量使用的一個(gè)技巧就是利用名稱映射來動態(tài)生成代碼, 形成一種類似模板的機(jī)制. 例如為了實(shí)現(xiàn)myWidth和myHeight兩個(gè)非常類似的函數(shù), 我們不需要
      jQuery.fn.myWidth = function(){
          return parseInt(this.style.width,10) + 10;
        }
        
        jQuery.fn.myHeight = function(){
          return parseInt(this.style.height,10) + 10;
        }
    而可以選擇動態(tài)生成
        jQuery.each(['Width','Height'],function(name){
          jQuery.fn['my'+name] = function(){
            return parseInt(this.style[name.toLowerCase()],10) + 10;
          }
        });
     
    12. 插件機(jī)制:其實(shí)我很簡單    
      jQuery所謂的插件其實(shí)就是$.fn上增加的函數(shù), 那這個(gè)fn是什么東西?
      (function(window,undefined){
        // 內(nèi)部又有一個(gè)包裝
        var jQuery = (function() {
          var jQuery = function( selector, context ) {
                return new jQuery.fn.init( selector, context, rootjQuery );
            }
           ....
          // fn實(shí)際就是prototype的簡寫
          jQuery.fn = jQuery.prototype = {
              constructor: jQuery,
              init: function( selector, context, rootjQuery ) {...  }
          }
        
          // 調(diào)用jQuery()就是相當(dāng)于new init(), 而init的prototype就是jQuery的prototype
          jQuery.fn.init.prototype = jQuery.fn;
        
          // 這里返回的jQuery對象只具備最基本的功能, 下面就是一系列的extend
          return jQuery;
        })();  
        ...
         // 將jQuery暴露為全局對象
        window.jQuery = window.$ = jQuery;
      })(window);
      顯然, $.fn其實(shí)就是jQuery.prototype的簡寫.
     
      無狀態(tài)的插件僅僅就是一個(gè)函數(shù), 非常簡單.
      // 定義插件
      (function($){
          $.fn.hoverClass = function(c) {
              return this.hover(
                  function() { $(this).toggleClass(c); }
              );
          };
      })(jQuery);
     
      // 使用插件
      $('li').hoverClass('hover');
     
     對于比較復(fù)雜的插件開發(fā), jQuery UI提供了一個(gè)widget工廠機(jī)制,
     $.widget("ui.dialog", {
       options: {
            autoOpen: true,...
         },
         _create: function(){ ... },
         _init: function() {
            if ( this.options.autoOpen ) {
                this.open();
            }
         },
         _setOption: function(key, value){ ... }
         destroy: function(){ ... }
     });
     
     調(diào)用 $('#dlg').dialog(options)時(shí), 實(shí)際執(zhí)行的代碼基本如下所示:
      this.each(function() {
            var instance = $.data( this, "dialog" );
            if ( instance ) {
                instance.option( options || {} )._init();
            } else {
                $.data( this, "dialog", new $.ui.dialog( options, this ) );
            }
        }
     可以看出, 第一次調(diào)用$('#dlg').dialog()函數(shù)時(shí)會創(chuàng)建窗口對象實(shí)例,并保存在data中, 此時(shí)會調(diào)用_create()和_init()函數(shù), 而如果不是第一次調(diào)用, 則是在已經(jīng)存在的對象實(shí)例上調(diào)用_init()方法. 多次調(diào)用$('#dlg').dialog()并不會創(chuàng)建多個(gè)實(shí)例.

    13. browser sniffer vs. feature detection
      瀏覽器嗅探(browser sniffer)曾經(jīng)是很流行的技術(shù), 比如早期的jQuery中
      jQuery.browser = {
            version:(userAgent.match(/.+(?:rv|it|ra|ie)[/: ]([d.]+)/) || [0,'0'])[1],
            safari:/webkit/.test(userAgent),
            opera:/opera/.test(userAgent),
            msie:/msie/.test(userAgent) && !/opera/.test(userAgent),
            mozilla:/mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent)
      };
      在具體代碼中可以針對不同的瀏覽器作出不同的處理
      if($.browser.msie) {
          // do something
      } else if($.browser.opera) {
          // ...
      }  

      但是隨著瀏覽器市場的競爭升級, 競爭對手之間的互相模仿和偽裝導(dǎo)致userAgent一片混亂, 加上Chrome的誕生, Safari的崛起, IE也開始加速向標(biāo)準(zhǔn)靠攏, sniffer已經(jīng)起不到積極的作用. 特性檢測(feature detection)作為更細(xì)粒度, 更具體的檢測手段, 逐漸成為處理瀏覽器兼容性的主流方式.
      jQuery.support = {
            // IE strips leading whitespace when .innerHTML is used
            leadingWhitespace: ( div.firstChild.nodeType === 3 ),
            ...
        }
        只基于實(shí)際看見的,而不是曾經(jīng)知道的, 這樣更容易做到兼容未來.

    14. Prototype vs. jQuery
      prototype.js是一個(gè)立意高遠(yuǎn)的庫, 它的目標(biāo)是提供一種新的使用體驗(yàn),參照Ruby從語言級別對javascript進(jìn)行改造,并最終真的極大改變了js的面貌。$, extends, each, bind...這些耳熟能詳?shù)母拍疃际莗rototype.js引入到j(luò)s領(lǐng)域的. 它肆無忌憚的在window全局名字空間中增加各種概念, 大有誰先占坑誰有理, 舍我其誰的氣勢. 而jQuery則扣扣索索, 抱著比較實(shí)用化的理念, 目標(biāo)僅僅是write less, do more而已.  
      不過等待激進(jìn)的理想主義者的命運(yùn)往往都是壯志未酬身先死. 當(dāng)prototype.js標(biāo)志性的bind函數(shù)等被吸收到ECMAScript標(biāo)準(zhǔn)中時(shí), 便注定了它的沒落. 到處修改原生對象的prototype, 這是prototype.js的獨(dú)門秘技, 也是它的死穴. 特別是當(dāng)它試圖模仿jQuery, 通過Element.extend(element)返回增強(qiáng)對象的時(shí)候, 算是徹底被jQuery給帶到溝里去了. prototype.js與jQuery不同, 它總是直接修改原生對象的prototype, 而瀏覽器卻是充滿bug, 謊言, 歷史包袱并夾雜著商業(yè)陰謀的領(lǐng)域, 在原生對象層面解決問題注定是一場悲劇. 性能問題, 名字沖突, 兼容性問題等等都是一個(gè)幫助庫的能力所無法解決的. Prototype.js的2.0版本據(jù)說要做大的變革, 不知是要與歷史決裂, 放棄兼容性, 還是繼續(xù)掙扎, 在夾縫中求生.

    posted @ 2011-12-25 21:23 canonical 閱讀(2381) | 評論 (5)編輯 收藏

    1. C語言抽象出了軟件所在的領(lǐng)域(domain): 由變量v1,v2,...和函數(shù)f1,f2,...組成的空間

    2. 面向?qū)ο?OOP)指出,在這一領(lǐng)域上可以建立分組(group)結(jié)構(gòu):一組相關(guān)的變量和函數(shù)構(gòu)成一個(gè)集合,我們稱之為對象(Object)。同時(shí)在分組結(jié)構(gòu)上可以定義一個(gè)運(yùn)算(推理)關(guān)系:  D > B, 派生類D從基類B繼承(inheritance),相應(yīng)的派生對象符合基類對象所滿足的所有約束。推理是有價(jià)值的,因?yàn)楦鶕?jù) D > B, B > A 可以自動推導(dǎo)出 D > A,所有針對A的斷言在理論上對D都成立(這也就是我們常說的“派生對象 is a 基類對象”)。編譯器也能有點(diǎn)智能了。
       一個(gè)有趣的地方是,D > B意味著在D和B之間存在著某種差異,但是我們卻無法把它顯式的表達(dá)出來!也就是說在代碼層面上我們無法明確表達(dá) D - B是什么。為了把更多的信息不斷的導(dǎo)入到原有系統(tǒng)中,面向?qū)ο髢?nèi)置提供的方法是建立不斷擴(kuò)展的類型樹,類型樹每增長一層,就可以多容納一些新的信息。這是一種金字塔式的結(jié)構(gòu),只不過是一種倒立的金字塔,最終基點(diǎn)會被不斷增長的結(jié)構(gòu)壓力所壓垮。

    3. 組件技術(shù)(Component)本質(zhì)上是在提倡面向接口(interface),然后通過接口之間的組合(Composition)而不是對象之間的繼承(inheritance)來構(gòu)造系統(tǒng)。基于組合的觀念相當(dāng)于是定義了運(yùn)算關(guān)系:D = B + C。終于,我們勉強(qiáng)可以在概念層面上做加法了。
       組件允許我們隨意的組合,按照由簡單到復(fù)雜的方向構(gòu)造系統(tǒng),但是組件構(gòu)成的成品之間仍然無法自由的建立關(guān)系。這意味著組件組裝得到的成品只是某種孤立的,偶然的產(chǎn)物。
       F = A + B + C  ? G = A + D + C。

    4. 在數(shù)學(xué)上,配備了加法運(yùn)算的集合構(gòu)成半群,如果要成為群(Group),則必須定義相應(yīng)的逆運(yùn)算:減法。 群結(jié)構(gòu)使得大粒度的結(jié)構(gòu)變換成為可能。
       F = A + B + C = A + D - D + B + C = (A + D + C) - D + B = G - D + B
       在不破壞原有代碼的情況下,對原有系統(tǒng)功能進(jìn)行增刪,這就是面向切面(AOP)技術(shù)的全部價(jià)值。


    posted @ 2011-05-08 12:07 canonical 閱讀(3679) | 評論 (1)編輯 收藏

       業(yè)務(wù)架構(gòu)平臺的設(shè)計(jì)與實(shí)現(xiàn)要比普通業(yè)務(wù)系統(tǒng)困難很多。一個(gè)核心難點(diǎn)在于如何建立普遍有效的應(yīng)用程序模型,如何控制各種偶然性的業(yè)務(wù)需求對系統(tǒng)整體架構(gòu)的沖擊。大多數(shù)現(xiàn)有的業(yè)務(wù)架構(gòu)平臺都是提供了一個(gè)龐大的萬能性產(chǎn)品,它預(yù)料到了所有可能在業(yè)務(wù)系統(tǒng)開發(fā)中出現(xiàn)的可能性,并提供了相應(yīng)的處理手段。業(yè)務(wù)系統(tǒng)開發(fā)人員的能力被限定在業(yè)務(wù)架構(gòu)平臺所允許的范圍之內(nèi)。如果業(yè)務(wù)架構(gòu)平臺的復(fù)雜度為A+,則我們最多只能用它來開發(fā)復(fù)雜度為A的業(yè)務(wù)系統(tǒng)。一個(gè)典型的特征就是使用業(yè)務(wù)架構(gòu)平臺的功能配置非常簡單,但是要開發(fā)相應(yīng)的功能特性則非常困難,而且必須采用與業(yè)務(wù)系統(tǒng)開發(fā)完全不同的技術(shù)手段和開發(fā)方式。
       采用業(yè)務(wù)架構(gòu)平臺來開發(fā)業(yè)務(wù)系統(tǒng),即使看似開發(fā)工作量小,最終產(chǎn)生的各類配置代碼量也可能會大大超過普通手工編程產(chǎn)生的代碼量,這意味著平臺封裝了業(yè)務(wù)內(nèi)在的復(fù)雜性,還是意味著平臺引入了不必要的復(fù)雜性?很多業(yè)務(wù)架構(gòu)平臺的賣點(diǎn)都是零代碼的應(yīng)用開發(fā),低水平的開發(fā)人員也可以主導(dǎo)的開發(fā),但是為什么高水平的程序員不能借助于這些開發(fā)平臺極大的提高生產(chǎn)率?
       一般的業(yè)務(wù)架構(gòu)平臺無法回答以下問題:
    1) 業(yè)務(wù)系統(tǒng)可以通過使用設(shè)計(jì)工具來重用業(yè)務(wù)架構(gòu)平臺已經(jīng)實(shí)現(xiàn)的功能,但是業(yè)務(wù)系統(tǒng)內(nèi)部大量相似的模型配置如何才能夠被重用?
    2) 特定的業(yè)務(wù)領(lǐng)域中存在著大量特殊的業(yè)務(wù)規(guī)則,例如“審批串行進(jìn)行,每一步都允許回退到上一步,而且允許選擇跳轉(zhuǎn)到任意后一步”。這些規(guī)則如何才能夠被引入設(shè)計(jì)工具,簡化配置過程?
    3) 已經(jīng)開發(fā)好的業(yè)務(wù)系統(tǒng)作為產(chǎn)品來銷售的時(shí)候,如何應(yīng)對具體客戶的定制化?如果按照客戶要求修改配置,則以后業(yè)務(wù)系統(tǒng)自身是否還能夠?qū)崿F(xiàn)版本升級?
      
       Witrix平臺提供的基本開發(fā)模型為
              App = Biz aop-extends Generator<DSL>
    在這一圖景下,我們就可以回答以上三個(gè)問題:
    1) 業(yè)務(wù)模型通過領(lǐng)域特定語言(DSL)來表達(dá),因此可以使用語言中通用的繼承或者組件抽象機(jī)制來實(shí)現(xiàn)模型重用。
    2) 推理機(jī)對于所有推理規(guī)則一視同仁,特殊的業(yè)務(wù)規(guī)則與通用的業(yè)務(wù)規(guī)則一樣都可以參與推理過程,并且一般情況下特殊的業(yè)務(wù)規(guī)則更能夠大幅簡化系統(tǒng)實(shí)現(xiàn)結(jié)構(gòu)。
    3) 相對于原始模型的修改被獨(dú)立出來,然后應(yīng)用面向切面(AOP)技術(shù)將這些特定代碼織入到原始模型中。原始模型與差異修改相互分離,因此原始模型可以隨時(shí)升級。

      Witrix平臺所強(qiáng)調(diào)的不是強(qiáng)大的功能,而是一切表象之后的數(shù)學(xué)規(guī)律。Witrix平臺通過少數(shù)基本原理的反復(fù)應(yīng)用來構(gòu)造軟件系統(tǒng),它本身就是采用平臺技術(shù)構(gòu)造的產(chǎn)物。我們用復(fù)雜度為A的工具制造復(fù)雜度為A+的產(chǎn)品,然后進(jìn)一步以這個(gè)復(fù)雜度為A+的產(chǎn)品為工具來構(gòu)造復(fù)雜度為A++的產(chǎn)品。

    posted @ 2011-02-11 14:02 canonical 閱讀(1570) | 評論 (0)編輯 收藏

        一種技術(shù)思想如果確實(shí)能夠簡化編程,有效降低系統(tǒng)構(gòu)造的復(fù)雜性,那么它必然具有某種內(nèi)在的數(shù)學(xué)解釋。反之,無論一種技術(shù)機(jī)制顯得如何華麗高深,如果它沒有 清晰的數(shù)學(xué)圖象,那么就很難證明自身存在的價(jià)值。對于模型驅(qū)動架構(gòu)(MDA),我長期以來一直都持有一種批判態(tài)度。(Physical Model Driven http://canonical.javaeye.com/blog/29412 )。原因就在于“由工具自動實(shí)現(xiàn)從平臺無關(guān)模型(PIM)向平臺相關(guān)模型(PSM)的轉(zhuǎn)換”這一圖景似乎只是想把系統(tǒng)從實(shí)現(xiàn)的泥沼中拯救出來,遮蔽特定語 言,特定平臺中的偶然的限制條件,并沒有觸及到系統(tǒng)復(fù)雜性這一核心問題。而所謂的可視化建模充其量不過是說明人類超強(qiáng)的視覺模式識別能力使得我們可以迅速 識別系統(tǒng)全景圖中隱含的整體結(jié)構(gòu),更快的實(shí)現(xiàn)對系統(tǒng)結(jié)構(gòu)的理解,并沒有證明系統(tǒng)復(fù)雜性有任何本質(zhì)性的降低。不過如果我們換一個(gè)視角, 不把模型局限為某種可視化的結(jié)構(gòu)圖,而將它定義為某種高度濃縮的領(lǐng)域描述, 則模型驅(qū)動基本上等價(jià)于根據(jù)領(lǐng)域描述自動推導(dǎo)得到最終的應(yīng)用程序。沿著這一思路,Witrix平臺中的很多設(shè)計(jì)實(shí)際上可以被解釋為模型定義,模型推導(dǎo)以及 模型嵌入等方面的探索。這些具體技術(shù)的背后需要的是比一般MDA思想更加精致的設(shè)計(jì)原理作為支撐。我們可以進(jìn)行如下抽象分析。(Witrix架構(gòu)分析 http://canonical.javaeye.com/blog/126467

    1. 問題復(fù)雜?線性切分是削減問題規(guī)模(從而降低問題復(fù)雜性)的通用手段,例如模塊(Module)。(軟件中的分析學(xué) http://canonical.javaeye.com/blog/33885

    App = M1 + M2 + M3 +    


    2. 分塊過多?同態(tài)映射是系統(tǒng)約化的一般化策略,例如多態(tài)(polymorphism)。(同構(gòu)與同態(tài):認(rèn)識同一性 http://canonical.javaeye.com/admin/blogs/340704

    (abc,abb,ade,-> [a],   (bbb, bcd,bab,-> [b]


    3. 遞歸使用以上兩種方法,將分分合合的游戲進(jìn)行到底,推向極致。

    4. 以少控多的終極形態(tài)?如果存在,則構(gòu)成輸入與輸出之間的非線性變換(輸入中局部微小變化將導(dǎo)致輸出中大范圍明顯的變化)。

    App = F(M)


    5. 變換函數(shù)F可以被詮釋為解釋器(Interpreter)或者翻譯機(jī),例如工作流引擎將工作流描述信息翻譯為一步步的具體操作,工作流描述可以看作是由底 層引擎支撐的,在更高的抽象層面上運(yùn)行的領(lǐng)域模型。但是在這種觀點(diǎn)下,變換函數(shù)F似乎是針對某種特定模型構(gòu)造的,引擎內(nèi)部信息傳導(dǎo)的途徑是確定的,關(guān)注的 重點(diǎn)始終在模型上,那么解釋器自身應(yīng)該如何被構(gòu)造出來呢?

    App ~ M


    6. 另外一種更加開放的觀點(diǎn)是將變換函數(shù)F看作是生成器(Generator)或者推理機(jī)。F將根據(jù)輸入的信息,結(jié)合其他知識,推理生成一系列新的命題和斷 言。模型的力量源于推導(dǎo)。變換函數(shù)F本身在系統(tǒng)構(gòu)造過程中處于核心地位,M僅僅是觸發(fā)其推理過程的信息源而已。F將榨干M的最后一點(diǎn)剩余價(jià)值,所有根據(jù)M 能夠確定的事實(shí)將被自動實(shí)現(xiàn),而大量單靠M自身的信息無法判定的命題也可以結(jié)合F內(nèi)在的知識作出判斷。生成器自身的構(gòu)造過程非常簡單--只要不斷向推理系 統(tǒng)中增加新的推理規(guī)則即可。語言內(nèi)置的模板機(jī)制(template)及元編程技術(shù)(meta programming),或者跨越語言邊界的代碼生成工具都可以看作是生成器的具體實(shí)例。(關(guān)于代碼生成和DSL http://canonical.javaeye.com/blog/275015 )

    App = G<M>


    7. 生成器G之所以可以被獨(dú)立實(shí)現(xiàn),是因?yàn)槲覀兛梢詫?shí)現(xiàn)相對知識與絕對知識的分離, 這也正是面向?qū)ο蠹夹g(shù)的本質(zhì)所在。(面向?qū)ο笾问较到y(tǒng) http://canonical.javaeye.com/blog/37064

    G<M> ==> G = {m => m.x(a,b,c);m.y();  }


    8. 現(xiàn)實(shí)世界的不完美,就在于現(xiàn)實(shí)決不按照我們?yōu)槠渲付ǖ睦硐肼肪€前進(jìn)。具體場景中總是存在著大量我們無法預(yù)知的“噪聲”,它們使得任何在“過去”確立的方程 都無法在“未來”保持持久的平衡。傳統(tǒng)模型驅(qū)動架構(gòu)的困境就在于此。我們可以選擇將模型M和生成器G不斷復(fù)雜化,容納越來越多的偶然性,直至失去對模型整 體結(jié)構(gòu)的控制力。另外一種選擇是模型在不斷膨脹,不斷提高覆蓋能力的過程中,不斷的空洞化,產(chǎn)生大量可插入(plugin)的接入點(diǎn),最終喪失模型的推理 能力,退化成為一種編碼規(guī)范。Witrix平臺中采用的是第三種選擇:模型嵌入--模型中的多余信息被不斷清洗掉,模型通過精煉化來突出自身存在的合理 性,成為更廣泛的運(yùn)行環(huán)境中的支撐骨架。(結(jié)構(gòu)的自足性 http://canonical.javaeye.com/blog/482620

    App != G0<M0>  , App != G0<M1>, App = G1<M1>


    9. 現(xiàn)在的問題是:如何基于一個(gè)已經(jīng)被完美解決的重大問題,來更有效率的搞定不斷出現(xiàn)但又不是重復(fù)出現(xiàn)的小問題。現(xiàn)在我們所需要的不是沿著某個(gè)維度進(jìn)行均勻的 切分,而必須是某種有效的降維手段。如果我們可以定義一種投影算子P, 將待解決的問題投射到已經(jīng)被解決的問題域中,則剩下的補(bǔ)集往往可以被簡化。(主從分解而不是正交分解 http://canonical.javaeye.com/blog/196826

    dA = App - P[App]  = App - G0<M0>


    10. 要實(shí)現(xiàn)以上微擾分析策略,前提條件是可以定義逆元,并且需要定義一種精細(xì)的粘結(jié)操作,可以將分散的擾動量極為精確的應(yīng)用到基礎(chǔ)系統(tǒng)的各處。Witrix平臺的具體實(shí)現(xiàn)類似于某種AOP(面向切面編程)技術(shù)。(逆元:不存在的真實(shí)存在 http://canonical.javaeye.com/blog/325051

    App = A + D + B = (A + B + C) - C + D = App0 + (-+ D) = G0<M0> + dA


    11. 模型驅(qū)動并不意味著一個(gè)應(yīng)用只能由唯一的一個(gè)模型來驅(qū)動,但是如果引入多個(gè)不同形式的模型,則必須為如下推理提供具體的技術(shù)路徑:
      A. 將多個(gè)模型變換到共同的描述域
      B. 實(shí)現(xiàn)多個(gè)模型的加和
      C. 處理模型之間的沖突并填補(bǔ)模型之間的空白
    在Witrix平臺中模型嵌入/組合主要依賴于文本化及編譯期運(yùn)行等機(jī)制。(文本化 http://canonical.javaeye.com/blog/309395

    App = Ga<Ma> + Gb<Mb> + dA


    12. 系統(tǒng)的開發(fā)時(shí)刻t0和實(shí)施時(shí)刻t1一般是明確分離的,因此如果我們要建立一個(gè)包含開發(fā)與實(shí)施時(shí)刻信息的模型,則這一模型必須是含時(shí)的,多階段的。關(guān)于時(shí) 間,我們所知道的最重要的事實(shí)之一是“未來是不可預(yù)知的”。在t0時(shí)刻建立的模型如果要涵蓋t1時(shí)刻的所有變化,則必須做出大量猜測,而且t1時(shí)刻距離 t0時(shí)刻越遠(yuǎn),猜測的量越大,“猜測有效”這一集合的測度越小,直至為0。延遲選擇是實(shí)現(xiàn)含時(shí)系統(tǒng)控制的不二法門。
       在Witrix平臺中,所有功能特性的實(shí)現(xiàn)都包含某種元數(shù)據(jù)描述或者定制模板,因此結(jié)合配置機(jī)制以及動態(tài)編譯技術(shù)既可實(shí)現(xiàn)多階段模型。例如對于一個(gè)在未來 才能確定的常量數(shù)組,我們可以定義一個(gè)Excel文件來允許實(shí)施人員錄入具體的值,然后通過動態(tài)編譯技術(shù)在編譯期解析Excel文件,并完成一系列數(shù)值映 射運(yùn)算,最終將其轉(zhuǎn)化為編譯期存在的一個(gè)常量。這一過程不會引入任何額外的運(yùn)行成本,也不要求任何特定的緩存機(jī)制,最終的運(yùn)行結(jié)構(gòu)與在未來當(dāng)所有信息都在 位之后再手寫代碼沒有任何區(qū)別。(D語言與tpl之編譯期動作 http://canonical.javaeye.com/blog/57244

    App(t1) = G(t0,t1)<M(t0,t1)> + dA(t0,t1)


    13. 級列理論提供了一個(gè)演化框架,它指出孤立的模型必須被放在模型級列中被定義,被解釋。(關(guān)于級列設(shè)計(jì)理論 http://canonical.javaeye.com/blog/33824

    M[n] = G<M[n-1]> + dMn


    14. 推理的鏈條會因?yàn)槿魏尉植糠蠢某霈F(xiàn)而中斷。在任意時(shí)空點(diǎn)上,我們能夠斷言的事實(shí)有哪些?信息越少,我們能夠確定的事實(shí)越少,能夠做出的推論也就越少。現(xiàn) 在流行的很多設(shè)計(jì)實(shí)質(zhì)上是在破壞系統(tǒng)的對稱性,破壞系統(tǒng)大范圍的結(jié)構(gòu)。比如明明ORM容器已經(jīng)實(shí)現(xiàn)所有數(shù)據(jù)對象的統(tǒng)一管理,非要將其拆分為每個(gè)業(yè)務(wù)表一個(gè) 的DAO接口。很多對靈活性的追求完全沒有搞清楚信息是對不確定性的消除,而不確定性的減少意味著限制的增加,約束的增加。(From Local To Global http://canonical.javaeye.com/blog/42874

       組件/構(gòu)件技術(shù)的宣言是生產(chǎn)即組裝,但是組裝有成本,有后遺癥(例如需要額外的膠水或者螺釘)。軟件的本質(zhì)并不是物質(zhì),而是信息,而信息的本質(zhì)是抽象的規(guī) 律。在抽象世界中最有效的生產(chǎn)方式是抽象的運(yùn)算,運(yùn)算即生產(chǎn)。組件式開發(fā)意味著服從現(xiàn)有規(guī)律,熟練應(yīng)用,而原理性生產(chǎn)則意味著不斷創(chuàng)造新的規(guī)律。功能模 塊越多,維護(hù)的成本越高,是負(fù)擔(dān),而推理機(jī)制越多,生產(chǎn)的成本越低,是財(cái)富。只有恢復(fù)軟件的抽象性,明確把握軟件構(gòu)造過程內(nèi)在的數(shù)學(xué)原理,才能真正釋放軟 件研發(fā)的生產(chǎn)力。(從編寫代碼到制造代碼 http://canonical.javaeye.com/blog/333167


    注解1:很多設(shè)計(jì)原則其實(shí)是在強(qiáng)調(diào)軟件由人構(gòu)造由人理解,軟件開發(fā)本質(zhì)上是人類工程學(xué),需要關(guān)注人類的理解力與協(xié)作能力。例如共同的建模語言減少交互成本,基于模型減少設(shè)計(jì)與實(shí)現(xiàn)的分離,易讀的代碼比高性能的代碼更重要,做一件事只有唯一的一種方式等。

    注解2:生成系統(tǒng)的演繹遠(yuǎn)比我們想象的要深刻與復(fù)雜得多。例如生命的成長可以被看作是在外界反饋下不斷調(diào)整的生成過程。

    注解3:領(lǐng)域描述是更緊致但卻未必是更本質(zhì)的表達(dá)。人類的DNA如果表達(dá)為ATGC序列完全可以拷貝到U盤中帶走,只要對DNA做少量增刪,就可以實(shí)現(xiàn)老 鼠到人類的變換(人類和老鼠都有大約30000條基因,其中約有80%的基因是“完全一樣的”,大約共享有99%的類似基因),但是很難認(rèn)為人類所有智慧 的本質(zhì)都體現(xiàn)在DNA中,DNA看起來更像是某種序列化保存形式而已。

    注解4:模型轉(zhuǎn)換這一提法似乎是在強(qiáng)調(diào)模型之間的同構(gòu)對應(yīng),轉(zhuǎn)換似乎總是可以雙向進(jìn)行的,僅僅是實(shí)現(xiàn)難度限制了反向轉(zhuǎn)換而已。但是大量有用的模型變換卻是單向的,變換過程要求不斷補(bǔ)充新的信息。

    注解5:模型驅(qū)動在很多人看來就是數(shù)據(jù)庫模型或者對象模型驅(qū)動系統(tǒng)界面運(yùn)行,但實(shí)際上模型可以意指任意抽象知識。雖然在目前業(yè)內(nèi)廣泛流行的對象范式下,所 有知識都可以表達(dá)為對象之間的關(guān)聯(lián),但是對象關(guān)系(名詞之間的關(guān)聯(lián)關(guān)系)對運(yùn)算結(jié)構(gòu)的揭示是遠(yuǎn)遠(yuǎn)不夠充分的。很多時(shí)候所謂的領(lǐng)域模型僅僅是表明概念之間具 有相關(guān)性,但是如果不補(bǔ)充一大段文字說明,我們對于系統(tǒng)如何運(yùn)作仍然一知半解。數(shù)學(xué)分析其實(shí)是將領(lǐng)域內(nèi)在的意義抽空,僅余下通用的形式化符號。

    posted @ 2011-02-07 02:56 canonical 閱讀(1538) | 評論 (0)編輯 收藏

        html主要通過內(nèi)置的<script>,<link>, <img>等標(biāo)簽引入外部的資源文件,一般的Web框架并沒有對這些資源文件進(jìn)行抽象,因此在實(shí)現(xiàn)組件封裝時(shí)存在一些難以克服的困難。例如一個(gè)使用傳統(tǒng)JSP Tag機(jī)制實(shí)現(xiàn)的Web組件中可能用到j(luò)s1.js, js2.js和css1.css等文件,當(dāng)在界面上存在多個(gè)同樣的組件的時(shí)候,可能會生成多個(gè)重復(fù)的<script>和<link>標(biāo)簽調(diào)用,這將對頁面性能造成嚴(yán)重的負(fù)面影響。資源管理應(yīng)該是一個(gè)Web框架的內(nèi)置組成部分之一。在Witrix平臺中,我們主要借助于tpl模板引擎來輸出html文本, 因此可以通過自定義標(biāo)簽機(jī)制重新實(shí)現(xiàn)資源相關(guān)的html標(biāo)簽, 由此來提供如下增強(qiáng)處理功能:

    1. 識別contextPath
       tpl模板中的所有資源相關(guān)標(biāo)簽都會自動拼接Web應(yīng)用的contextPath, 例如當(dāng)contextPath=myApp時(shí)
       <script src="/a.js"></script> 將最終輸出 <script src="/myApp/a.js" ...>

    2. 識別重復(fù)裝載
       <script src="a.js" tpl:once="true"></script>
       tpl:once屬性將保證在頁面中script標(biāo)簽實(shí)際只會出現(xiàn)一次.

    3. 識別組件內(nèi)相對路徑
      開發(fā)Web組件時(shí),我們希望所有資源文件都應(yīng)該相對組件目錄進(jìn)行定位,但是直接輸出的<script>等標(biāo)簽都是相對于最終的調(diào)用鏈接進(jìn)行相對路徑定位的. 例如在page1.jsp中調(diào)用了組件A, 在組件A的實(shí)現(xiàn)中, 輸出了<script src="my_control.js"></script>
     我們的意圖一般是相對于組件A的實(shí)現(xiàn)文件進(jìn)行定位, 而不是相對于page1.jsp進(jìn)行定位. tpl模板引擎的相對路徑解析規(guī)則為永遠(yuǎn)相對于當(dāng)前文件進(jìn)行定位. 例如
      <c:include src="sub.tpl" />
    在sub.tpl中的所有相對路徑都相對于sub.tpl文件進(jìn)行定位.

    4. 編譯期文件有效性檢查
       在編譯期, tpl引擎會檢查所有引入的資源文件的有效性. 如果發(fā)現(xiàn)資源文件丟失, 將直接拋出異常. 這樣就不用等到上線后才發(fā)現(xiàn)文件命名已修改等問題.

    5. 緩存控制
      瀏覽器缺省會緩存css, js等文件, 因此系統(tǒng)上線后如果修改資源文件可能會造成與客戶端緩存不一致的情況. 一個(gè)簡單的處理方式是每次生成資源鏈接的時(shí)候都拼接文件的修改日期或者版本號, 這樣既可利用客戶端緩存, 又可以保證總是使用最新版本. 例如
      <script src="a.js"></script>將會輸出 <script src="/myApp/myModule/a.js?344566" ...>

    6. 字符集選擇
      為了簡化國際化處理, 一般提倡的最佳實(shí)踐方式是堅(jiān)持使用UTF-8編碼. 但是很多情況下可能使用系統(tǒng)內(nèi)置的GBK編碼會更加方便一些, 另外集成一些既有代碼時(shí)也存在著不同字符集的問題. 在Witrix平臺中, 所有輸出的資源標(biāo)簽都會標(biāo)明對應(yīng)的字符集, 如果沒有明確設(shè)置就取系統(tǒng)參數(shù)中的缺省字符集.
     例如 <script src="a.js"></script> 將會輸出 <script ... charset="GBK"></script>

    7. 缺省theme支持
      為了支持多種頁面風(fēng)格, 往往不是簡單的替換css文件即可實(shí)現(xiàn)的, 它可能意味著整個(gè)組件的實(shí)現(xiàn)代碼的更換. Witrix平臺中通過一系列缺省判斷來簡化這一過程. 例如如下代碼表明如果設(shè)置了ui_theme系統(tǒng)參數(shù), 并且對應(yīng)的特殊實(shí)現(xiàn)存在, 則使用特殊實(shí)現(xiàn), 否則系統(tǒng)缺省實(shí)現(xiàn).
      <c:include src="${cp:ui_theme()}/ctl_my_ctl.tpl" >
        <c:include src="default/ctl_my_ctl.tpl" />
      </c:include>

    posted @ 2010-01-17 17:51 canonical 閱讀(1345) | 評論 (0)編輯 收藏

         AOP(Aspect Oriented Programming)早已不是什么新鮮的概念,但有趣的是,除了事務(wù)(transaction), 日志(Log)等寥寥幾個(gè)樣板應(yīng)用之外,我們似乎找不到它的用武之地。http://canonical.javaeye.com/blog/34941 很多人的疑惑是我直接改代碼就行了,干嗎要用AOP呢?AOP的定義和實(shí)現(xiàn)那么復(fù)雜,能夠提供什么特異的價(jià)值呢?
        Witrix平臺依賴于AOP概念來完成領(lǐng)域模型抽象與模型變換,但是在具體的實(shí)現(xiàn)方式上,卻與常見的AOP軟件包有著很大差異。http://canonical.javaeye.com/blog/542622 AOP的具體技術(shù)內(nèi)容包括定位和組裝兩個(gè)部分。簡化切點(diǎn)定位方式和重新規(guī)劃組裝空間,是Witrix中有效使用AOP技術(shù)的前提。
        在Witrix平臺中,對于AOP技術(shù)的一種具體應(yīng)用是支持產(chǎn)品的二次開發(fā)。在產(chǎn)品的實(shí)施過程中,經(jīng)常需要根據(jù)客戶的特定需求,修改某些函數(shù)的實(shí)現(xiàn)。我們 可以選擇在主版本代碼中不斷追加相互糾纏的if-else語句,試圖去包容所有已知和未知的應(yīng)用場景。我們也可以選擇主版本代碼和定制代碼獨(dú)立開發(fā)的方 式,主版本代碼實(shí)現(xiàn)邏輯框架,定制代碼通過AOP機(jī)制與主版本代碼融合,根據(jù)具體場景要求對主版本功能進(jìn)行修正。AOP的這種應(yīng)用與所謂的橫切概念是有所 區(qū)別的。典型的,一個(gè)橫切的切點(diǎn)會涉及到很多類的很多方法,而函數(shù)定制則往往要求準(zhǔn)確定位到某個(gè)業(yè)務(wù)對象的某個(gè)特定的業(yè)務(wù)方法上。傳統(tǒng)AOP技術(shù)的切點(diǎn)定 義方式并不適合這種精確的單點(diǎn)定位。在Witrix平臺中,我們通過直接的名稱映射來定義切點(diǎn)。例如,修正spring中注冊的MyObject對象的 myFunc方法,可以在app.aop.xml文件中增加如下標(biāo)簽

    <myObject.myFunc>
          在原函數(shù)執(zhí)行之前執(zhí)行
          
    <aop:Proceed/> <!-- 執(zhí)行原函數(shù)內(nèi)容 -->
          在原函數(shù)執(zhí)行之后執(zhí)行
    </myObject.myFunc>


    [spring對象名.方法名]這種映射方法比基于正則字符串匹配的方式要簡單明確的多。spring容器本身已經(jīng)實(shí)現(xiàn)了對象的全局管理功能,spring對象名稱必然是唯一的,公開發(fā)布的,相互之間不沖突的,沒有必要再通過匹配運(yùn)算重新發(fā)現(xiàn)出它的唯一性。
       對于一些確實(shí)存在的橫切需求,我們可以通過Annotation機(jī)制來實(shí)現(xiàn)切點(diǎn)坐標(biāo)標(biāo)定,將復(fù)雜的切點(diǎn)匹配問題重新劃歸為[對象名.方法名]。

    @AopClass({"myObject","otherObject"})
      class SomeClass{
         @AopMethod({"myFunc","otherFunc"})
         void someFunc(){}
      }

     

    針對以上對象,在app.aop.xml文件中可以定義

    <I-myObject.I-myFunc>
       .
    </I-myObject.I-myFunc>

    posted @ 2009-12-13 11:34 canonical 閱讀(1525) | 評論 (0)編輯 收藏

       結(jié)構(gòu)的穩(wěn)定性,直觀的理解起來,就是結(jié)構(gòu)在存在外部擾動的情況下長時(shí)間保持某種形式不變性的能力。穩(wěn)定意味著小的擾動造成的后果也是“小”的。在數(shù)學(xué)中,Taylor級數(shù)為我們描繪了變化傳播的基本圖景。

     
    F(x0 + dx) = F(x0) + F'(x0)*dx + 0.5*F''(x0)*dx^2 + 

    擾動dx可能在系統(tǒng)F中引發(fā)非常復(fù)雜的作用過程,在系統(tǒng)各處產(chǎn)生一個(gè)個(gè)局部變化結(jié)果。表面上看起來,似乎這些變化結(jié)果存在著無窮多種可能的分組方式,例如 (F'(x0)-2)*dx + 2*dx^2, 但是基于微分分析,我們卻很容易了解到Taylor級數(shù)的每一級都對應(yīng)著獨(dú)立的物理解釋,它們構(gòu)成自然的分組標(biāo)準(zhǔn)。某一量級下的所有變化匯總歸并到一起,并對應(yīng)一個(gè)明確的整體描述。在抽象的數(shù)理空間中,我們具有一種無所不達(dá)的變化搜集能力。變化項(xiàng)可以從基礎(chǔ)結(jié)構(gòu)中分離出來,經(jīng)過匯總后可以對其進(jìn)行獨(dú)立的研究。變化本身并不會直接導(dǎo)致基礎(chǔ)結(jié)構(gòu)的崩潰。
       在軟件建模領(lǐng)域,模型的穩(wěn)定性面臨的卻是另一番場景。一個(gè)軟件模型一旦被實(shí)現(xiàn)之后,種種局部需求變更就都會形成對原有基礎(chǔ)結(jié)構(gòu)的沖擊。一些局部的需求變化可能造成大片原有實(shí)現(xiàn)失效,我們將被迫為類似的需求重新編寫類似的代碼。此時(shí),軟件開發(fā)并不像是一種純粹的信息創(chuàng)造,而是宛若某種物質(zhì)產(chǎn)品的生產(chǎn)(參見從編寫代碼到制造代碼 http://canonical.javaeye.com/blog/333167 )。顯然,我們需要一種能力,將局部變化從基礎(chǔ)結(jié)構(gòu)中剝離出來,經(jīng)過匯總歸并之后再進(jìn)行綜合分析和處理。這正是AOP(Aspect Oriented Programming)技術(shù)的價(jià)值所在。

       
    M1 = (G0+dG0)<M0+dM0> ==> M1 = G0<M0> + dM
      AOP本質(zhì)上是軟件結(jié)構(gòu)空間的自由修正機(jī)制。只有結(jié)合AOP技術(shù)之后,軟件模型才能夠重新恢復(fù)抽象的本質(zhì),在時(shí)間之河中逃離隨機(jī)變化的侵蝕,保持實(shí)現(xiàn)層面的穩(wěn)定性。在這一背景下,建模的目的將不是為了能夠跟蹤最終需求的變動,而是要在某個(gè)獨(dú)立的層面上能夠自圓其說,能夠具有某種獨(dú)立存在的完滿性,成為思維上可以把握的某個(gè)穩(wěn)定的基點(diǎn)。模型的真實(shí)性將因?yàn)樽陨斫Y(jié)構(gòu)的完備性而得到證明,與外部世界的契合程度不再是價(jià)值判斷的唯一標(biāo)準(zhǔn)。http://canonical.javaeye.com/blog/482620

    posted @ 2009-12-06 12:23 canonical 閱讀(1222) | 評論 (0)編輯 收藏

       說到軟件建模,一個(gè)常見的論調(diào)是模型應(yīng)該符合實(shí)際需求,反映問題的本質(zhì)。但是何謂本質(zhì),卻是沒有先驗(yàn)定義的。在成功的建立一個(gè)模型之前,無論在內(nèi)涵上還是在外延上我們都很難說清楚一個(gè)問題的本質(zhì)是什么。如果將模型看作是對領(lǐng)域結(jié)構(gòu)的一種顯式描述和表達(dá),我們可以首先考察一下一個(gè)“合適”的結(jié)構(gòu)應(yīng)該具備哪些特征。
       按照結(jié)構(gòu)主義哲學(xué)的觀點(diǎn),結(jié)構(gòu)具有三個(gè)要素:整體性,具有轉(zhuǎn)換規(guī)律或法則(轉(zhuǎn)換性),自身調(diào)整性(自律性)。整體性意味著結(jié)構(gòu)不能被簡單的切分,其構(gòu)成要素通過內(nèi)在的關(guān)系運(yùn)算實(shí)現(xiàn)大范圍的關(guān)聯(lián)與轉(zhuǎn)換,整體之所以成為整體正是以轉(zhuǎn)換/運(yùn)算的第一性為保證的。這種轉(zhuǎn)換可以是共時(shí)的(同時(shí)存在的各元素),也可以是歷時(shí)的(歷史的轉(zhuǎn)換構(gòu)造過程),這意味著結(jié)構(gòu)總要求一個(gè)內(nèi)在的構(gòu)造過程,在獨(dú)立于外部環(huán)境的情況下結(jié)構(gòu)具有某種自給自足的特性,不依賴于外部條件即可獨(dú)立的存在并保持內(nèi)在的活動。自律性意味著結(jié)構(gòu)內(nèi)在的轉(zhuǎn)換總是維持著某種封閉性和守恒性,確保新的成分在無限地構(gòu)成而結(jié)構(gòu)邊界卻保持穩(wěn)定。注意到這里對結(jié)構(gòu)的評判并不是來自外在規(guī)范和約束,而是基于結(jié)構(gòu)內(nèi)在的規(guī)律性,所強(qiáng)調(diào)的不是結(jié)構(gòu)對外部條件的適應(yīng)性,而是自身概念體系的完備性。實(shí)際上,一個(gè)無法直接對應(yīng)于當(dāng)前實(shí)際環(huán)境的結(jié)構(gòu)仍然可能具有重要的價(jià)值,并在解決問題的過程中扮演不可或缺的角色。在合理性這個(gè)視角下,我們所關(guān)注的不僅僅是當(dāng)前的現(xiàn)實(shí)世界,而是所有可能的世界。一個(gè)“合理”的結(jié)構(gòu)的價(jià)值必能在它所適應(yīng)的世界中凸現(xiàn)出來。
       在信息系統(tǒng)中,我們可能經(jīng)常會問這個(gè)模型是否是對業(yè)務(wù)的準(zhǔn)確描述,是否可以適應(yīng)需求的變更,是否允許未來的各種擴(kuò)展等等。但是如果換一個(gè)思維方向,我們會發(fā)現(xiàn)這些問題都是針對最終確立的模型而發(fā)問的,而在模型構(gòu)建的過程中,那些可被利用的已存在的或者可以存在的模型又是哪些呢。每一個(gè)信息模型都對應(yīng)著某種自動推理機(jī),可以接收信息并做一定的推導(dǎo)綜合工作。一個(gè)可行的問題是,如何才能更有效的利用已有的信息進(jìn)行推導(dǎo),如何消除冗余并減少各種轉(zhuǎn)換成本。我們經(jīng)常可以觀察到,某一信息組織方式更充分的發(fā)掘了信息之間的內(nèi)在關(guān)聯(lián)(一個(gè)表象是它對信息的使用不是簡單的局域化的,而是在多處呈現(xiàn)為互相糾纏的方式,難以被分解),這種內(nèi)在關(guān)聯(lián)足夠豐富,以至于我們不依賴于外部因素就可以獨(dú)立的理解。這種糾纏在一起的信息塊自然會成為我們建模的對象。
       如果模型的“覆蓋能力”不再是我們關(guān)注的重點(diǎn),那么建模的思維圖式將會發(fā)生如下的轉(zhuǎn)化


    最終的模型可以由一系列微模型交織構(gòu)成。模型的遞進(jìn)構(gòu)造過程并不同于組件(Component)的實(shí)物組裝接口,也不是CAD圖紙堆疊式的架構(gòu)概念所能容納的。在Witrix平臺中,模型分解和構(gòu)造表達(dá)為如下形式  http://canonical.javaeye.com/blog/333167
         Biz[n] = Biz[n+1] aop-extends CodeGenerator<DSLx, DSLy>。
       在軟件發(fā)展的早期,所有的程序都是特殊構(gòu)造的,其必然的假設(shè)是【在此情況下】,重用不在考慮范圍之內(nèi),開發(fā)可以說是一個(gè)盲目試錯(cuò)的過程。隨著我們逐步積累了一些經(jīng)驗(yàn),開始自覺的應(yīng)用理論分析手段,【在所有情況下】都成立的一些普適的原理被揭示出來,它們成為我們在廣闊的未知世界中跋涉時(shí)的向?qū)А.?dāng)我們的足跡漸漸接近領(lǐng)域的邊界,對領(lǐng)域的全貌有一個(gè)總體的認(rèn)知之后,一種對自身成熟性的自信很自然的將我們導(dǎo)向更加領(lǐng)域特定的分析。很多時(shí)候,我們會發(fā)現(xiàn)一個(gè)特化假設(shè)可以大大提高信息的利用率,推導(dǎo)出眾多未被顯式設(shè)定的知識。我們需要那些【在某些情況下】有效的規(guī)則來構(gòu)成一個(gè)完備的模型庫。這就如同有大量備選的數(shù)學(xué)定理,面對不同的物理現(xiàn)象,我們會從一系列的數(shù)學(xué)工具中選擇一個(gè)進(jìn)行使用一樣。
      


    posted @ 2009-10-07 17:10 canonical 閱讀(2408) | 評論 (0)編輯 收藏

        軟件開發(fā)技術(shù)的技術(shù)本質(zhì)在于對代碼結(jié)構(gòu)的有效控制. 我們需要能夠有效的分解/重組代碼片斷, 凸顯設(shè)計(jì)意圖. 面向?qū)ο笫悄壳白畛R姷拇a組織技術(shù). 典型的, 它可以處理如下模式
      A1 --> B2,  A2 --> B2,  A3 --> B3 ...
    我們觀察到A1和A2之間, B2和B2之間具有某種概念關(guān)聯(lián)性, 同時(shí)存在某種抽象結(jié)構(gòu) [A] --> [B].
    對于這種情況, 我們可以定義對象 [A], [B], 它們分別是 A1和A2的聚合, B1和B2的聚合等. 舉例來說, 對于如下表格描述, <ui:Col>所提供的信息在映射為html實(shí)現(xiàn)的時(shí)候?qū)⒃诙嗵幈粦?yīng)用.
    <ui:Table data="${data}">
      <ui:Col name="fieldA" label="labelA" width="20" />
      <ui:Col name="fieldB" label="labelB" width="10" /> 
    </ui:Table>
    這里<ui:Col>提供的信息對應(yīng)三個(gè)部分的內(nèi)容: 1. 列標(biāo)題 2. 列樣式(寬度等)  3. 列數(shù)據(jù)

    面向?qū)ο蟮某R娮龇ㄊ浅橄蟪?UiCol對象, 它作為UiTable對象的屬性存在, 在生成表頭, 表列樣式和表格數(shù)據(jù)內(nèi)容時(shí)將被使用. 但是我們注意到面向?qū)ο笠蠖鄠€(gè)方法通過this指針形成狀態(tài)耦合

    ,在某種意義上它意味著所有的成員方法在任一時(shí)刻都是同時(shí)存在著的。它們所代表著的存在的代價(jià)必須被接受(存儲空間等)。即使并不同時(shí)被使用,我們?nèi)匀恍枰瑫r(shí)持有所有成員函數(shù)指針及

    共享的this指針。實(shí)際上, 我們并不一定需要A1和A2同時(shí)在場. 在這種情況下, 編譯期技術(shù)可以提供另一種不同的行為聚合方式.


    <table>
      <thead>
        <sys:CompileTagBody cp:part="thead" />
      </thead>
      <cols>
        <sys:CompileTagBody cp:part="cols" />
      </cols>
      <tbody>
        <sys:CompileTagBody cp:part="tbody" />
      </tbody>
    </table>

    只要<ui:Col>標(biāo)簽的實(shí)現(xiàn)中針對編譯期的cp:part變量進(jìn)行分別處理, 即可實(shí)現(xiàn)信息的部分析取.


    posted @ 2009-07-11 21:37 canonical 閱讀(1240) | 評論 (0)編輯 收藏

      html最早的設(shè)計(jì)目標(biāo)只是作為某種多媒體文檔展現(xiàn)技術(shù),其設(shè)計(jì)者顯然無法預(yù)料到今天Web應(yīng)用的蓬勃發(fā)展,一些設(shè)計(jì)缺陷也就難以避免。特別是html規(guī)范中缺乏對于復(fù)雜交互式組件模型的支持,直接導(dǎo)致企業(yè)應(yīng)用的前臺開發(fā)困難重重。AJAX技術(shù)可以看作是對這種困境的一種改良性響應(yīng),它試圖通過javascript語言在應(yīng)用層創(chuàng)建并維護(hù)一系列復(fù)雜的交互機(jī)制。很多完善的ajax框架走得相當(dāng)遙遠(yuǎn),最終基本將html作為一種底層“匯編”語言來使用。例如,一個(gè)很整齊美觀的類Excel表格可能是由一個(gè)個(gè)div拼接而成,與html原生的table元素已經(jīng)沒有任何關(guān)系。
     
       Witrix平臺中對于前臺html模型也作了一定的增強(qiáng),但基本的設(shè)計(jì)思想是盡量利用原生控件,并盡量保持原生控件內(nèi)在的數(shù)據(jù)關(guān)系,而不是重新構(gòu)建一個(gè)完整的底層支撐環(huán)境。采用這種設(shè)計(jì)的原因大致有如下幾點(diǎn):
    1. 前臺技術(shù)目前競爭非常激烈,我們優(yōu)先選擇的方式是集成第三方組件,盡量保持原生環(huán)境有利于降低集成成本。
    2. 通過javascript構(gòu)造的控件可能存在性能瓶頸和其他瀏覽器內(nèi)在的限制。例如一般Ajax框架提供的Grid控件都無法支撐大量單元格的顯示。
    3. Witrix平臺的tpl模板技術(shù)可以非常方便的生成html文本,并提供強(qiáng)大的控件抽象能力,因此在前臺動態(tài)創(chuàng)建并組織界面元素在Witrix平臺中是一種不經(jīng)濟(jì)的做法。
    4. Witrix平臺提供的分解機(jī)制非常細(xì)致,存儲于不同地方的不同來源的代碼會在不同的時(shí)刻織入到最終的頁面中,基于原生環(huán)境有利于降低平臺快速演進(jìn)過程中的設(shè)計(jì)風(fēng)險(xiǎn)。

       Witrix平臺中對于html模型的增強(qiáng)主要關(guān)注于以最少的代碼實(shí)現(xiàn)界面控件與業(yè)務(wù)邏輯的自然結(jié)合。基本結(jié)構(gòu)包括:
    1. 通過ControlManager對象在前臺建立一種container結(jié)構(gòu),統(tǒng)一管理控件的注冊和獲取。js.makeControl(elmOrId)返回特殊注冊的控件對象或者根據(jù)原生html元素生成一個(gè)包裝對象。
    2. 通過js.getWxValue(elm)和js.setWxValue(elm,value)這兩個(gè)函數(shù)統(tǒng)一對控件的值的存取過程。
    3. 通過js.regListener(elm,listenerFunc)統(tǒng)一管理控件之間的相關(guān)觸發(fā),實(shí)現(xiàn)控件之間的相互監(jiān)聽。當(dāng)js.setWxValue(elm,value)被調(diào)用時(shí),注冊在ControlManager中的listenerFunc將被調(diào)用。
    4. stdPage.setFieldValue(fieldName,value)和stdPage.getFieldValue(fieldName,value)統(tǒng)一針對業(yè)務(wù)字段的值的存取過程,這里fieldName對應(yīng)于實(shí)體上的業(yè)務(wù)字段名。
    5. 通過ajax.addForm(frmId)等函數(shù)統(tǒng)一前臺提交參數(shù)的提取過程,通過stdPage.buildAjax()等函數(shù)統(tǒng)一后臺服務(wù)的調(diào)用方式。
    6. 通過stdPage對象統(tǒng)一封裝業(yè)務(wù)場景中的"常識"。
    基于以上一些基礎(chǔ)機(jī)制,Witrix平臺即可提供一些復(fù)雜的業(yè)務(wù)組件封裝。例如<input name="productCode" onkeypress="stdPage.keyPressToLoadRefByCode({objectName:'SomeProduct',queryField:'productCode'})" .../>通過簡單的調(diào)用一個(gè)js函數(shù)即可實(shí)現(xiàn)如下功能:
    a. 在文本框中輸入回車的時(shí)候自動提交到后臺查找對應(yīng)產(chǎn)品代碼的產(chǎn)品,并更新前臺多個(gè)相關(guān)字段的值
    b. 如果沒有查找到相應(yīng)產(chǎn)品,則彈出對話框根據(jù)界面上已有的部分字段信息提示客戶添加新的產(chǎn)品信息。
    c. 如果找到多個(gè)對應(yīng)產(chǎn)品,則彈出列表允許客戶選擇其一。
    d. 具體的處理過程可以通過函數(shù)參數(shù)進(jìn)行精細(xì)的控制。
    在meta文件中,結(jié)合上下文環(huán)境中的元數(shù)據(jù)信息,我們在缺省情況下可以直接使用 <ds:LoadRefByCodeInputor objectName="SomeProduct" />標(biāo)簽,不需要任何其他附加參數(shù)。

       Witrix平臺中一般利用原生控件來保存數(shù)據(jù)值,而不是將數(shù)據(jù)保存在分離的js對象中。例如對于一個(gè)選擇控件,經(jīng)常要求選擇得到的是實(shí)體的id,而顯示在界面上的是某個(gè)其他字段的值。Witrix平臺中一般的實(shí)現(xiàn)結(jié)構(gòu)是
       <input type="hidden" name="${fieldName}" value="${entity[dsMeta.idField]}" id="${id}" textId="text_${id}" />
       <input type="text" value="${entity[dsMeta.nameField]}" id="text_${id}" />
    通過textId等擴(kuò)展屬性即可明確定義控件多個(gè)部分之間的關(guān)聯(lián)關(guān)系,同時(shí)保證控件的實(shí)現(xiàn)完全與html規(guī)范相兼容。
       Witrix平臺中目前使用的"標(biāo)準(zhǔn)化"的擴(kuò)展屬性有textId(對應(yīng)文本顯示控件的id), showName(某些無文字顯示的選擇控件需要保留顯示字段值), op(字段作為查詢條件提交時(shí)的比較算符),validator(字段值對應(yīng)的檢驗(yàn)函數(shù)),setWxValue/getWxValue(重定義控件值的存取行為),serializer(特殊處理前臺控件的提交參數(shù))等。擴(kuò)展屬性不僅可以引入說明信息,還可以引入豐富的控件行為。
      


    posted @ 2009-05-30 00:44 canonical 閱讀(2712) | 評論 (2)編輯 收藏

        分層是最常見的軟件架構(gòu)方式之一。分層之后可以區(qū)分出橫縱兩個(gè)維度,縱向往往表現(xiàn)出一種隔離性。出于有意無意的各種原因,層次之間傳遞信息很容易出現(xiàn)模糊甚至丟失的現(xiàn)象。B/S多層體系架構(gòu)下的程序因?yàn)闉g覽器和服務(wù)器之間的狀態(tài)空間相互獨(dú)立,相對于共享全局狀態(tài)空間的C/S程序,更容易出現(xiàn)信息傳遞不暢的問題。實(shí)際上,我們經(jīng)常可以觀察到B/S程序中存在著大量的"接力"代碼,即在交界處,總是存在著大量用于讀取變量,拼接變量,轉(zhuǎn)換變量等與主體業(yè)務(wù)無關(guān)但卻又不可或缺的代碼。在多層架構(gòu)程序中,信道構(gòu)建應(yīng)該是一個(gè)需要給予足夠重視的問題。

        在系統(tǒng)規(guī)劃中,多層結(jié)構(gòu)應(yīng)該內(nèi)置與具體語義無關(guān)的通用信道,它跨越多個(gè)層次,允許信息透明的通過,并以未預(yù)期的方式在不同的層面激發(fā)各種相關(guān)的行為。在Witrix平臺中,平臺代碼與特定應(yīng)用中的業(yè)務(wù)代碼處于高度交織的狀態(tài),一個(gè)特定業(yè)務(wù)功能的實(shí)現(xiàn)往往需要多處業(yè)務(wù)代碼相互協(xié)同,平臺必須成為某種透明的背景。例如,假設(shè)我們編制了一個(gè)通用的列表選擇控件,它封裝的邏輯是從一個(gè)實(shí)體列表中進(jìn)行選擇
          <app:SelectOne objectName="MyEntity" />
    如果現(xiàn)在要求選擇時(shí)只列出某個(gè)類型的實(shí)體,則調(diào)用形式為
          <app:SelectOne objectName="MyEntity" extArgs="$bizId=select&amp;$type=1" />
    在調(diào)用入口處補(bǔ)充必要的信息之后會推動系統(tǒng)在遙遠(yuǎn)的狀態(tài)空間中應(yīng)用一個(gè)特定的過濾條件。這里$bizId負(fù)責(zé)指示平臺應(yīng)用特定的元數(shù)據(jù)配置,而其他的參數(shù)則由元數(shù)據(jù)中的邏輯負(fù)責(zé)處理。平臺與特定業(yè)務(wù)代碼各取所需,相互配合,將盡可能多的邏輯剝離為通用機(jī)制。


    posted @ 2009-03-22 21:10 canonical 閱讀(628) | 評論 (0)編輯 收藏

    javaeye提供的電子書制作功能非常實(shí)用。
    http://www.tkk7.com/Files/canonical/canonical-blog-20090228.rar

    posted @ 2009-02-28 17:21 canonical 閱讀(448) | 評論 (0)編輯 收藏

       現(xiàn)代數(shù)學(xué)是建立在等價(jià)類這一概念的基礎(chǔ)之上的。同構(gòu)是對等價(jià)關(guān)系的一種刻劃。簡單的可以把它理解為兩個(gè)系統(tǒng)之間的一種“保持”運(yùn)算規(guī)則的一一對應(yīng)關(guān)系。在 數(shù)學(xué)中一個(gè)符號所代表的是所有能夠互相同構(gòu)的對象。例如整數(shù)3可以看作是與某個(gè)元素個(gè)數(shù)為3的集合可以建立一一對應(yīng)關(guān)系的所有的集合所構(gòu)成的整體。所以在 數(shù)學(xué)中,如果我們解決某個(gè)特定的問題,它同時(shí)也就意味著我們解決了一系列相互等價(jià)的問題。
        同構(gòu)關(guān)系對于認(rèn)知可以起到本質(zhì)上的簡化作用。如果通過一個(gè)推理鏈條,確認(rèn)了A == B == C == D,則可以直接從概念上推導(dǎo)出 A == D, 這一關(guān)系有可能被直觀理解,而不需要理會中間的推理步驟。(注意到以上元素兩兩建立同構(gòu)關(guān)系的時(shí)候可能要采用不同的對應(yīng)手段,因此上面的等式并不是平凡 的。)另一方面,我們可以確定一個(gè)模型元素M,  將系統(tǒng)簡化為 A == M, B == M, C == M, D == M。只要理解了元素M就理解了等價(jià)的其他元素。

        Witrix平臺中PDM定義作為基礎(chǔ)的結(jié)構(gòu)模型,它同時(shí)映射成為數(shù)據(jù)庫表,以及hbm, java, meta等多個(gè)代碼文件,此外還對應(yīng)于約定的WebObject名稱和BizFlow文件名稱,相應(yīng)的報(bào)表文件目錄等。我們只要理解了pdm模型,即可通 過推理自然的掌握各個(gè)層面上對應(yīng)的結(jié)構(gòu)。這意味著只要知道實(shí)體名稱,就知道如何通過Web訪問這個(gè)對象,知道數(shù)據(jù)在數(shù)據(jù)庫中對應(yīng)的數(shù)據(jù)庫表,而不需要知道 后臺是如何讀取前臺提交的參數(shù)以及如何執(zhí)行保存數(shù)據(jù)指令的。不僅僅是在模型驅(qū)動領(lǐng)域,在系統(tǒng)設(shè)計(jì)的各個(gè)方面,我們都應(yīng)該盡量充分的利用當(dāng)前的信息通過推理 得到系統(tǒng)其他部分的結(jié)構(gòu),而不是通過手工關(guān)聯(lián)或者判斷在程序中動態(tài)維持這種對應(yīng)關(guān)系。例如在flow-cp機(jī)制中,biz的id, action的id等都根據(jù)step配置的id推導(dǎo)得到,這樣在工作列表跳轉(zhuǎn)的時(shí)候就可以根據(jù)規(guī)則推導(dǎo)出跳轉(zhuǎn)頁面對應(yīng)的鏈接,而不需要手工編寫頁面重定向 代碼。




     
        同態(tài)(homomorphism)關(guān)系相對于同構(gòu)關(guān)系,只強(qiáng)調(diào)單向映射的可行性,它是一個(gè)舍棄屬性的過程。同態(tài)作為最基礎(chǔ)的認(rèn)知手段之一,它不僅僅是用一 個(gè)符號來置換一組元素,而是同時(shí)保留了某種全局的運(yùn)算關(guān)系,因此同態(tài)映像可以構(gòu)成某種獨(dú)立的完整的研究對象。通過同態(tài)映射,我們可以在不同的抽象層面上研 究原系統(tǒng)的一個(gè)簡化版本。例如meta中的layout是一種典型的領(lǐng)域特定語言(DSL)。
        userName userTitle
        emailAddress

    每一個(gè)字段表示了一個(gè)可能任意復(fù)雜的inputor或者viewer, 字段之間的前后關(guān)系描述了最終顯示頁面上顯示內(nèi)容的相對關(guān)系。當(dāng)viewer根據(jù)需求發(fā)生改變的時(shí)候,并不影響到layout層面上的關(guān)系,因此 layout可以保持不變。如果我們在系統(tǒng)中把問題分解為多個(gè)抽象層面上,多個(gè)觀察視角上的同態(tài)模型,則可能實(shí)現(xiàn)更高的軟件復(fù)用程度。
        在Witrix平臺的設(shè)計(jì)中,很多細(xì)粒度的標(biāo)簽都定義為tpl文本段,這樣平臺只要理解某一層面上的交互關(guān)系,實(shí)際應(yīng)用中可能出現(xiàn)的細(xì)節(jié)在標(biāo)簽內(nèi)部進(jìn)行局 部處理,不會突破原始設(shè)計(jì)的形式邊界,不會影響到原先設(shè)定的主體系統(tǒng)結(jié)構(gòu)。例如BizFlow中的tpls段,action的source段等。
        上世紀(jì)50年代以前,生物學(xué)家做夢也想象不到具有無限復(fù)雜性的生物遺傳過程,竟然可以被抽象為ATGC四個(gè)抽象符號的串聯(lián)。生命竟然不理會各種已知的或是 未知的物理化學(xué)作用,被抽象的三聯(lián)碼所驅(qū)動。一種抽象的本質(zhì)似乎成了生命世界的本原。在軟件的世界中,可以被識別的抽象元素絕不只是語言本身所提供的那些 機(jī)制。

    posted @ 2009-02-28 16:57 canonical 閱讀(1646) | 評論 (0)編輯 收藏

          有一個(gè)心理學(xué)實(shí)驗(yàn),要求被試者將青草,公雞,牛三個(gè)東西分 成兩組,結(jié)果多數(shù)中國兒童將青草和牛分成一組,而多數(shù)美國兒童將公雞和牛分成一組。中國人的思想中青草和牛之間存在現(xiàn)實(shí)的關(guān)系,牛吃草,而西方人的典型邏 輯是公雞和牛都屬于動物這一范疇。通過分類將物體類型化,這是西方人從小就接受的訓(xùn)練。據(jù)說美國嬰兒學(xué)習(xí)名詞的速度要快于動詞,而中國的嬰兒則相反,這并不是偶然的。

           中國人的傳統(tǒng)哲學(xué)認(rèn)為世界是普遍聯(lián)系的,事物之間存在著禍福相依的辯證轉(zhuǎn)化關(guān)系。而古希臘人強(qiáng)調(diào)個(gè)體意識,以兩分法看待世界,他們將世界看成是孤立的物體組成(原子論)構(gòu)成,然后選擇一個(gè)孤立物體(脫離背景),開始研究它的各項(xiàng)屬性,接著將屬性泛化,構(gòu)成分類的基礎(chǔ)。西方語言中大量抽象概念都是從作為屬性的形容詞直接轉(zhuǎn)化而來,例如 white --> whiteness 。而中文中很少有精確的類型定義,而多半是富有表現(xiàn)力的,隱喻性的詞語,例如我們不談?wù)摮橄蟮陌祝徽f雪白,沒有抽象的 size ,而只說具體的大小。

           亞里士多德認(rèn)為鐵球在空氣中下落是因?yàn)樗哂?#8220;重性”,而木塊在水中漂浮是因?yàn)槟緣K具有“輕性”。這種將一切原因歸結(jié)為事物內(nèi)在屬性的傳統(tǒng)在一定程度上妨礙了西方人認(rèn)識到背景的存在和作用,但使得他們可以把問題簡化。

           古希臘人對于類型的熱衷源于他們對于永恒的迷戀。靜態(tài)的亙古不變的世界才是他們的思想棲息的場所。具體的物體是易逝的,多變的,只有抽象的類型才是永恒的存在,也只有抽象概念之間的關(guān)系才是永真的聯(lián)系。而具體實(shí)例之間的關(guān)聯(lián)在某種程度上被認(rèn)為是不重要的,甚至是不可靠的。



           將具有某一屬性的所有物體定義為一個(gè)集合,這一做法在上世紀(jì)初被發(fā)現(xiàn)會引起邏輯悖論,動搖了整個(gè)數(shù)學(xué)的基礎(chǔ),它絕不像表面上看起來那么單純。但確定無疑的是,通過類型來把握不變的事實(shí)是一種非常重要且有效的認(rèn)識策略。面向?qū)ο笳Z言強(qiáng)調(diào)名詞概 念,從引入類定義以及類之間的繼承關(guān)系開始,這符合西方一貫的作風(fēng)。而 Ruby 這種強(qiáng)調(diào)實(shí)例間關(guān)系的動態(tài)語言首先由日本人發(fā)明,可能也不是偶然的。雖然現(xiàn)在大家都在玩高科技了,可實(shí)際販賣給你的多半仍然是包治百病的祖?zhèn)髅胤健N幕赡茉斐烧J(rèn)知上的一種偏執(zhí),在技術(shù)領(lǐng)域這一現(xiàn)象并沒有被清楚的意識到。

     

    posted @ 2009-02-21 19:43 canonical 閱讀(1762) | 評論 (2)編輯 收藏

        軟件開發(fā)作為一種工程技術(shù),它所研究的一個(gè)重點(diǎn)就是如何才能有效降低軟件產(chǎn)品的研發(fā)成本。在這一方向上,組件技術(shù)取得了空前的成功。它所提供的基本圖景是 像搭積木一樣從無到有的組裝出最終的產(chǎn)品。在某種意義上,這是對現(xiàn)代建筑工業(yè)的模仿和致敬。新材料,預(yù)制件,框架結(jié)構(gòu),這些建筑學(xué)的進(jìn)展在軟件領(lǐng)域被一一 復(fù)制,建筑工地上的民工自然也成為了程序員們學(xué)習(xí)的楷模。畢竟,在組件的世界中碼代碼,基本上也是一種“搬磚”的行為。

        值得慶幸的是,軟件開發(fā)作為一種智力活動,它天生具有一種“去民工化”的傾向。信息產(chǎn)品有著與物質(zhì)世界產(chǎn)品完全不同的抽象本質(zhì)。在物理空間中,建造100 棟房屋,必須付出100倍的努力,老老實(shí)實(shí)的干上100遍。而在概念空間中建造100棟房屋,我們卻可以說其他房屋與第一棟一模一樣,加點(diǎn)平移旋轉(zhuǎn)變換即 可。一塊磚填了地基就不能用來蓋屋頂,而一段寫好的代碼卻可以在任何可用的場合無損耗的被使用。一棟建好的房屋發(fā)現(xiàn)水管漏水要大動干戈,而在完成的軟件中 補(bǔ)個(gè)局部bug卻是小菜一碟。在抽象的世界中有效的進(jìn)行生產(chǎn),所依賴的不應(yīng)該是大干,苦干的堆砌,而應(yīng)該是發(fā)現(xiàn)某種可用于推導(dǎo)的原理,基于這些原理,輸入 信息可以立刻轉(zhuǎn)換為最終的結(jié)果,而不需要一個(gè)逐步構(gòu)造的過程。即我們有可能超越組裝性生產(chǎn),實(shí)現(xiàn)某種類似于數(shù)學(xué)的原理性生產(chǎn)。http://canonical.javaeye.com/blog/325051

        代碼復(fù)用是目前軟件業(yè)中鼓吹降低生產(chǎn)成本的主要口號之一。但是在組件技術(shù)的指向下,一般所復(fù)用的只是用于構(gòu)建的磚塊,而并不是某種構(gòu)造原理。即使在所有信 息已經(jīng)確定的情況下,我們?nèi)匀徊豢赡軓男枨罅⒖痰玫娇蓤?zhí)行的產(chǎn)品。很多代碼即使我們在想象中已經(jīng)歷歷在目,卻仍然需要一行行把它們謄寫下來。當(dāng)我們發(fā)現(xiàn)系 統(tǒng)中已經(jīng)沒有任何組件值得抽象的時(shí)候,仍然留下來眾多的工作需要機(jī)械化的執(zhí)行。代碼復(fù)用的理想國距離我們?nèi)匀环浅5倪b遠(yuǎn)。

        子例程(subroutine)是最早的代碼重用機(jī)制。這就像是將昨天已經(jīng)完成的工作錄制下來,在需要的時(shí)候重新播放。函數(shù)(function)相對于子 例程更加強(qiáng)大。很多代碼看起來并不一樣,但是如果把其中的差異部分看作是變量,那么它們的結(jié)構(gòu)就可以統(tǒng)一了。再加上一些判斷和循環(huán),很多面目迥異的東西其 實(shí)是存在著大量共享信息的。面向?qū)ο蠹夹g(shù)是一次飛躍性的發(fā)展。眾多相關(guān)信息被打包到一個(gè)名稱(類型)中,復(fù)用的粒度和復(fù)雜度都大大提升。派生類從基類繼 承,可以通過重載實(shí)現(xiàn)對原有代碼的細(xì)致調(diào)整。不過,這種方式仍然無法滿足日益增長的復(fù)用需求。很多時(shí)候,一個(gè)名稱并不足以標(biāo)定我們最終需要的代碼結(jié)構(gòu),在 實(shí)際使用的時(shí)候還需要補(bǔ)充更多的信息。類型參數(shù)化,即泛型技術(shù),從事后的角度看其實(shí)是一種明顯的解決方案。根據(jù)參數(shù)動態(tài)的生成基類自然可以吸納更多的變 化。經(jīng)歷了所謂Modern C++的發(fā)展之后,我們現(xiàn)在已經(jīng)非常明確,泛型并非僅僅能夠?qū)崿F(xiàn)類型共變,而是可以從類型參數(shù)中引入更豐富的結(jié)構(gòu)信息,它的本質(zhì)是一種代碼生成的過程。http://canonical.javaeye.com/blog/57244 認(rèn)清了這一點(diǎn),它的擴(kuò)展就非常明顯了

       BaseClass<ArgClass> --> CodeGenerator<DSL>

    DSL(或者某種模型對象)相對于普通的類型(Class),信息密度要大很多,它可以提供更豐富也更完整的輸入信息。而CodeGenerator也不必拘泥于基礎(chǔ)語言本身提供的各種編譯機(jī)制,而是可以靈活應(yīng)用各種文本生成技術(shù)。http://canonical.javaeye.com/blog/309395 CodeGenerator在這里所提供的正是根據(jù)輸入模型推導(dǎo)出完整實(shí)現(xiàn)代碼的構(gòu)造原理。

        現(xiàn)在很多人熱衷于開發(fā)自己的簡易代碼生成工具,這些工具也許可以在簡單的情形下減輕一些體力工作,但是生成的代碼一般不能直接滿足需求,仍然需要手工執(zhí)行 大量的刪改工作。當(dāng)代碼生成之后,它成為一種固化的物質(zhì)產(chǎn)品,不再能夠隨著代碼生成工具的改進(jìn)而同步改進(jìn),在長期的系統(tǒng)演化過程中,這些工具并不一定能夠 減少累積的工作量。

       修正過程  ==> CodeGenerator<DSL>

    為了改進(jìn)以上代碼生產(chǎn)過程,一些人試圖在CodeGenerator中引入越來越多的可配置性,將各種變化的可能內(nèi)置在構(gòu)造原理中。顯然這會造成CodeGenerator的不正常的腫脹。當(dāng)更多的偶然性被包含在原理中的時(shí)候,必然會破壞原理的簡單性和普適性。

       輸入信息 + 一段用于推導(dǎo)的原理 + 修正補(bǔ)充 = 真實(shí)模型

    必須存在[修正補(bǔ)充]這一項(xiàng)才能維持以上方程的持久平衡。

        為了擺脫人工修正過程,將模型調(diào)整納入到概念世界中,我們需要超越繼承機(jī)制的,更加強(qiáng)大的,新的技術(shù)手段。其實(shí)在當(dāng)前的技術(shù)背景下,這一技術(shù)已經(jīng)是呼之欲出了。這就是AOP, Aspect Oriented Programming。http://canonical.javaeye.com/blog/34941

          Biz ==[AOP extends]==> CodeGenerator<DSL>

    繼承僅僅能夠?qū)崿F(xiàn)同名方法之間的簡單覆蓋,而AOP所代表的技術(shù)原理卻是在代碼結(jié)構(gòu)空間中進(jìn)行任意復(fù)雜的刪改操作,它潛在的能力等價(jià)于人工調(diào)整。

        為了實(shí)現(xiàn)上述生產(chǎn)模式,需要對編程語言,組件模型,框架設(shè)計(jì)等方面進(jìn)行一系列改造。目前通用的AOP實(shí)現(xiàn)和元編程技術(shù)其實(shí)并不足以支持以上模式。http://canonical.javaeye.com/blog/275015
        這一生產(chǎn)模式將會如何演化,也是一個(gè)有趣的問題。按照級列理論,我們立刻可以得到如下發(fā)展方向:

        Context0 + DSL1 + EXT0 = DSL0  
        Context1 
    + DSL2 + EXT1 = DSL1 
       

     http://canonical.javaeye.com/blog/33824

    Witrix平臺中BizFlow可以看作是對DaoWebAction的修正模型,但是它本身具有完整的意義,可以直觀的被理解。在BizFlow的基礎(chǔ)上可以逐步建立SeqFlow,StateFlow等模型。http://canonical.javaeye.com/blog/126467

          現(xiàn)在有些人試圖深挖函數(shù)式語言,利用模式匹配之類的概念,做符號空間的全局優(yōu)化。但是我們需要認(rèn)識到通用的機(jī)制是很少的,能夠在通用語言背景下被明確提出 的問題更是很少的。只有在特定領(lǐng)域中,在掌握更多局部信息的情況下,我們才能提出豐富的問題,并作出一定背景下的解答。DSL的世界中待做的和可做的工作 很多。http://canonical.javaeye.com/blog/147065

          對于程序員而言,未來將變得越來越豐富而復(fù)雜,它將持續(xù)拷問我們的洞察力。我們不是一行行的編寫代碼,把需求一條條的翻譯到某種實(shí)現(xiàn)上,而是不斷發(fā)明局部的生產(chǎn)原理,依靠自己制定的規(guī)則在抽象的空間中不斷的創(chuàng)造新的表象。

    posted @ 2009-02-15 18:21 canonical 閱讀(2092) | 評論 (2)編輯 收藏

           負(fù)數(shù)沒有直接的幾何意義,因此它被認(rèn)為是對應(yīng)于不存在的事物。而按照古希臘的邏輯,不存在的事物是不可能存在的,因而也就是無法被理解的,更不可能參與到 推理過程中,因此是無意義的,無法被定義的, 因此它是不存在的。中國人注重的是運(yùn)算的合理性,而不是數(shù)的真理性,大概在公元前400年左右就創(chuàng)造了負(fù)數(shù)和零的概念。而在西方直到公元7世紀(jì)(唐代)的 一本印度人的著作中才出現(xiàn)負(fù)數(shù),它被用來表示負(fù)債。西方人沒有能夠創(chuàng)造負(fù)數(shù),他們對負(fù)數(shù)的接受更遲至15世紀(jì)左右。這件事實(shí)在一定程度上說明了存在某種深 刻的困難阻礙我們理解負(fù)數(shù)概念。
           在引入負(fù)數(shù)之前,3x^2 + 8 = 4x 和  3x^2 + 4x + 8 = 0 這兩個(gè)方程的結(jié)構(gòu)是完全不同的,它們需要不同的求解技術(shù),因此也就不可能利用符號抽象出 a x^2 + b x + c = 0。引入負(fù)數(shù)才使得我們能夠以統(tǒng)一的方式提出問題,并研究通用的求解技術(shù)。
          群論(Group Theory)是對結(jié)構(gòu)進(jìn)行抽象研究的數(shù)學(xué)分支。群的定義包括四條規(guī)則
    1.    元素之間的運(yùn)算滿足結(jié)合律 (a * b) * c = a * (b * c)
    2.    元素之間的運(yùn)算封閉,即 a * b 仍然屬于該群
    3.    存在單位元,即對所有a, a * e = e*a = a
    4.    每個(gè)元素存在對應(yīng)的逆元,a * a^-1= e
          逆運(yùn)算是非常重要的結(jié)構(gòu)要求,逆元是對負(fù)數(shù)的一種抽象推廣。如果沒有逆元,則只能構(gòu)成半群(semi-group),它的性質(zhì)要少很多。


          目前軟件設(shè)計(jì)中所有的原則都指向組裝過程,從無到有,層層累進(jìn)。構(gòu)件組裝的隱喻中所包含的圖像是操縱實(shí)際可見的積木,是缺少逆元概念的。

          考察一個(gè)簡單的例子,假設(shè)需要的產(chǎn)品是三角形內(nèi)部挖去一個(gè)五邊形的剩余部分。有三種生產(chǎn)策略:
    1.    對最終需要的產(chǎn)品形態(tài)進(jìn)行三角剖分,使用8個(gè)小三角形拼接出來。這種方式比較繁瑣,而且最后粘接工序的可靠性和精確性值得懷疑。
    2.    拿到一個(gè)真實(shí)的三角形,然后用刀在內(nèi)部挖出一個(gè)五邊形的洞。這種方式需要消耗一定的人工,而且也很難保證五邊形的精確性,即使我們曾經(jīng)精確的生產(chǎn)過其他五角形和三角形。實(shí)際上一般情況下我們是逐步銼出一個(gè)五邊形的,并沒有充分利用到五邊形的對稱性。
    3.    在概念空間中做一個(gè)抽象計(jì)算  (-五邊形) + (三角形) = 所需產(chǎn)品
    如果我們能夠生產(chǎn)一種負(fù)的五邊形和一種正的三角形,則可以立刻得到最終的產(chǎn)品。



     
    在軟件開發(fā)的實(shí)踐中,我們目前多數(shù)采用的是兩種方式:
    1.    采用可視化設(shè)計(jì)工具通過拖拽操作開發(fā)出完整的界面和后臺
    2.    拷貝一些已有的代碼,刪除掉不需要的部分,增加一些新的實(shí)現(xiàn),也可能對已有實(shí)現(xiàn)做一些不兼容的修正。

    在第二種方式中
               新結(jié)構(gòu)的構(gòu)造 = 已有結(jié)構(gòu) + 軟件之外的由人執(zhí)行的一個(gè)剪裁過程
    這個(gè)剪裁過程表現(xiàn)為一個(gè)時(shí)間序列。如果我們對原有結(jié)構(gòu)進(jìn)行了調(diào)整,則需要重新關(guān)聯(lián)一個(gè)時(shí)間序列,而此時(shí)間序列并不會自動重播。為了壓縮以時(shí)間為度量單位的 生產(chǎn)成本,我們必須減少對時(shí)間序列的依賴。在時(shí)間序列中展開的一個(gè)構(gòu)造過程可以被轉(zhuǎn)化為一個(gè)高維設(shè)計(jì)空間中的一種更加豐富的構(gòu)造原理,我們最終的觀測可以 看作是設(shè)計(jì)空間向物理空間的一個(gè)投影(想象一束光打下來)。這種方式更容易保證程序的正確性。
              時(shí)間序列 --[原理轉(zhuǎn)化]--> 空間關(guān)系


        這樣我們就可以使用第三種生產(chǎn)策略:利用構(gòu)造原理進(jìn)行抽象計(jì)算。如果我們只盯著產(chǎn)品的最終形態(tài)看,只是想著怎么把它像搭積木一樣搭建出來,就不可能識別出 系統(tǒng)結(jié)構(gòu)本身所蘊(yùn)含的對稱性。如果我們發(fā)現(xiàn)了系統(tǒng)內(nèi)蘊(yùn)的結(jié)構(gòu)特征,但是卻只能通過構(gòu)造過程中的行動序列來追隨它,同樣無法實(shí)現(xiàn)有效的工作復(fù)用。同時(shí)因?yàn)檫@ 個(gè)行動序列一般處于系統(tǒng)規(guī)則約束之外,完全由人的自覺來保障,因此很難保證它的正確性。現(xiàn)實(shí)世界的規(guī)范要求并不是模型本身所必須滿足的,只要我們能夠創(chuàng)造 新的結(jié)構(gòu)原理,在概念空間中我們就可以擁有更多的自由。現(xiàn)在業(yè)內(nèi)鼓吹的軟件構(gòu)造原理多半是參照物理世界中生產(chǎn)具體物質(zhì)產(chǎn)品的生產(chǎn)工序,卻沒有真正把握信息的抽象本質(zhì)。掌握規(guī)則,制訂規(guī)則,才是信息空間中的游戲規(guī)則。

        物理學(xué)中最重要的分析學(xué)思想之一是微擾論(Perturbation). 針對一個(gè)復(fù)雜的物理現(xiàn)象,首先建立一個(gè)全局的規(guī)范的模型,然后考慮各種微擾條件對原有模型的影響。在小擾動情況下,模型的變化部分往往可以被線性化,被局 域化,因而問題得到簡化。微擾分析得到的解依賴于全局模型的解而存在,因而這是一種主從關(guān)系的分解方式。但是如果主體模型是我們已經(jīng)熟知的物理現(xiàn)象,則我 們關(guān)注的重點(diǎn)可以全部放在擾動解上,認(rèn)為所有特定的物理規(guī)律都體現(xiàn)在擾動解中。如果微擾分析得到的物理元素足夠豐富,則微擾模型本身可以成為獨(dú)立的研究對 象,在其中我們同樣可以發(fā)現(xiàn)某種普適的結(jié)構(gòu)規(guī)律。
        考察如下的構(gòu)造過程
           X = a + b + c
           Y = a + b + d = (a + b + c) - c + d = X - c + d
        對于數(shù)學(xué)而言,上述的推導(dǎo)是等價(jià)的,但是對于物理學(xué)而言,Y = a + b + d 和  Y = X - c + d是有著本質(zhì)不同的。第一種方式要求打破原先X的構(gòu)造,而重新的組裝其實(shí)是有成本的,特別是在X本身非常復(fù)雜的情況下。典型的,如果X是經(jīng)過測試的功能, 重新組裝后原先由測試保障的概念邊界被打破。
             我們可以從Y = X + dX抽象出擾動模型  dX = - c + d
    主從分解模式自然的導(dǎo)出逆元概念。

          如果沒有逆元,我們必然需要分解。但是如果發(fā)掘了背景這一概念,在逆元運(yùn)算下,對背景不是分解讓其成為可見的部分,而是采用追加的,增刪的方法對背景結(jié)構(gòu) 進(jìn)行修正,則我們有可能在沒有完整背景知識的情況下,獨(dú)立的理解局部變化的結(jié)構(gòu)。即背景是透明的,知識成為局部的。在Witrix平臺中,BizFlow + DaoWebAction + StdPage 才構(gòu)成完整的程序模型,BizFlow其實(shí)是對標(biāo)準(zhǔn)模型的差異描述,但是它可以被單獨(dú)的理解。如果我們從接觸程序開始就接受BizFlow, 就可能完全不需要了解數(shù)據(jù)庫訪問和前臺界面渲染的知識。我們并不是通過在DaoWebAction中設(shè)定各種可預(yù)見的調(diào)用形式,而是在BizFlow中通 過類似AOP的操作方式直接對標(biāo)準(zhǔn)模型進(jìn)行修正。這種修正中一個(gè)很重要的部分就是刪除標(biāo)準(zhǔn)模型中缺省提供的功能。
         WebMVC之前世今生 http://canonical.javaeye.com/blog/163196
         Witrix架構(gòu)分析 http://canonical.javaeye.com/blog/126467


          變化的部分構(gòu)成獨(dú)立于原始模型的新的模型,它的結(jié)構(gòu)關(guān)系是完備的,可以獨(dú)立的理解。在原始模型崩潰的情況下,它仍然可能保持有效性。
          從物理學(xué)的角度看,我們所觀測到的一切物理現(xiàn)象,都是某種物理作用的結(jié)果,也就是物質(zhì)結(jié)構(gòu)相對于背景狀況的一種偏離。我們只可能觀測到變化的部分,因此我們對世界的認(rèn)識其實(shí)只是世界的擾動模型而已,世界的本體不屬于科學(xué)研究的范疇。

    posted @ 2009-02-07 22:22 canonical 閱讀(1610) | 評論 (1)編輯 收藏

        軟件技術(shù)的發(fā)展是一個(gè)結(jié)構(gòu)化不斷加深的過程,我們逐漸擁有了越來越豐富的結(jié)構(gòu)識別, 表達(dá)和處理手段。在這一方向上, 組件技術(shù)最終取得了巨大的商業(yè)成功。但是區(qū)分同時(shí)也就意味著隔閡。面向?qū)ο蠹夹g(shù)最基礎(chǔ)的概念在于 O = C(O), 對象的復(fù)合(Composition)仍然是對象.  然而在越來越復(fù)雜的軟件生態(tài)環(huán)境中,這一圖景實(shí)現(xiàn)的可能性被大大的壓縮. 面對層層封裝造成的形態(tài)各異的表達(dá)方式, 不同來源, 不同目標(biāo), 不同運(yùn)行環(huán)境下的信息的交互變得越來越困難。我們逐漸喪失了概念上的簡潔性, 也喪失了數(shù)學(xué)世界中的那種穿透一切的統(tǒng)一的力量. 所謂SOA(Serivce Oriented Architecture)技術(shù)試圖通過補(bǔ)充更多的環(huán)境信息,放棄狀態(tài)關(guān)聯(lián),暴露元知識等方式來突破現(xiàn)有的困境。 http://canonical.javaeye.com/blog/33803 這其中一項(xiàng)關(guān)鍵的技術(shù)抉擇是基于文本格式進(jìn)行信息表達(dá)。

          在過去10年中Web技術(shù)取得了空前的成功,它造就了互聯(lián)網(wǎng)這一世界上最大的分布式集成應(yīng)用。SOA從某種程度上說是對這一事實(shí)在技術(shù)層面上的反思。基于 文本傳遞的web技術(shù)所表現(xiàn)出來的開放性,可理解性和可觀測性,與封閉的,難以直接解讀的,必須擁有大量相關(guān)知識才能正確操作的二進(jìn)制結(jié)構(gòu)相比,這本身就 是革命性的創(chuàng)新。不需要特殊的工具就可以非常輕易的察看到網(wǎng)頁程序的輸入輸出,所有交互過程都處在完全透明的檢查下,各種不曾事先規(guī)劃的文本處理手段都可 以參與到web創(chuàng)建的過程中。隨著速度,存儲不再是我們考慮的首要問題,文本化作為一種技術(shù)趨勢逐漸變得明確起來。但是任何一種技術(shù)思想的成功或失敗都不 可能是因?yàn)槟撤N單一的原因所造成的,因此有必要對文本化的技術(shù)價(jià)值做更加細(xì)致的剖析。
      
       1. 文本化是一定程度上的去結(jié)構(gòu)化,但是通過這種方式我們獲得了對程序結(jié)構(gòu)的更強(qiáng)的控制力。無論我們所關(guān)心的文本片斷層層嵌套在大段文本的哪個(gè)部分,我們都可 以通過text.indexOf(subStr)來實(shí)現(xiàn)定位,通過text.replace(oldStr,newStr)實(shí)現(xiàn)變換。而在組件系統(tǒng)中,我 們只有通過某種預(yù)定的遍歷方式逐步遞歸才有可能訪問到組件內(nèi)部的組成對象。如果某個(gè)組件不按照規(guī)范實(shí)現(xiàn)或者規(guī)范中缺少明確的設(shè)定,則這一信息關(guān)聯(lián)鏈條將會 斷裂。在一些組件系統(tǒng)中,甚至沒有規(guī)定一種統(tǒng)一的遍歷方式,則我們根本無法定義出通用的大范圍的結(jié)構(gòu)定位/變換手段。即使是在設(shè)計(jì)精良的組件框架中,受限 制于組件的封裝性要求,我們也不可能訪問到全部的信息。當(dāng)我們以組件設(shè)計(jì)者意料之外的方式使用這些組件的時(shí)候,就會遇到重重障礙。即使所需的只是微小的局 部調(diào)整,因?yàn)闊o法觸及到需要修改的部分,我們也可能被迫放棄整個(gè)組件。
      
       2. 文本是普適的信道。各種操作系統(tǒng),各種程序語言所具有的最基本的能力就是文本處理能力,對于文本格式的約定是最容易達(dá)成共識的。雖然對于機(jī)器而言,理解基 于地址定位的二進(jìn)制數(shù)字可能更加直接,但是所有的應(yīng)用程序都是由人來負(fù)責(zé)編制,調(diào)試,部署,維護(hù)的,如果人可以不借助特殊的工具就可以明確了解到系統(tǒng)內(nèi)發(fā) 生的過程,則系統(tǒng)的構(gòu)建和維護(hù)成本就會大幅下降。
      
       3. 文本表述形式上的冗余性增強(qiáng)了系統(tǒng)的概念穩(wěn)定性和局部可理解性。在二進(jìn)制格式中,經(jīng)常出現(xiàn)的是根據(jù)相對地址定位,這要求我們完整理解整個(gè)二進(jìn)制結(jié)構(gòu),才能 夠逐步定位到局部數(shù)據(jù)塊。同時(shí),二進(jìn)制格式中也經(jīng)常使用一些外部的信息,例如某個(gè)位置處的數(shù)據(jù)為整型,占用四個(gè)字節(jié)等。這樣的信息可能只能在文檔說明里查 到,而在數(shù)據(jù)體中沒有任何的體現(xiàn),這限制了獨(dú)立的理解可能性。與此相反,文本格式經(jīng)常是自說明式的,例如width:3.5px, 這提高了系統(tǒng)的可理解性,特別是局部可理解性。即使我們對系統(tǒng)只具有很少的知識,一般也能根據(jù)數(shù)據(jù)周圍的相關(guān)信息進(jìn)行局部操作。一般很容易就能夠定位到特 殊的局部數(shù)據(jù)區(qū),安全的跳過眾多未知的或者不被關(guān)心的結(jié)構(gòu). 一個(gè)程序新手也可以在很短時(shí)間內(nèi)靠著連蒙帶猜, 實(shí)現(xiàn)xml格式的word文件中的書簽替換功能,而要搞清楚word的二進(jìn)制格式,并獨(dú)立編制出正確的替換功能,顯然就不是一兩周的工作量可以解決的了. 這其中, 可理解性的差異是存在著數(shù)量級上的鴻溝的.
      
       4. xml這種半結(jié)構(gòu)化的文本格式規(guī)范的興起, 以通用的方式為文本描述引入了基本的形式約束, 實(shí)現(xiàn)了結(jié)構(gòu)表達(dá)的均一性. C語言中的宏(Macro)本質(zhì)上就是一種文本替換技術(shù),它的威力在于沒有預(yù)定義的語義, 因此可以超越其他語法成分, 破除現(xiàn)有語法無法跨越的限制. 但是它的危險(xiǎn)性在于缺乏與其能力相適應(yīng)的形式約束, 難以控制. 而在xml格式規(guī)范下, 不同語義, 不同抽象層面的節(jié)點(diǎn)可以共存在同一個(gè)形式體系中, 可以用通用的方式進(jìn)行定位,校驗(yàn), 轉(zhuǎn)換等. Witrix平臺在動態(tài)xml方面發(fā)展了一系列技術(shù), 為文本處理引入了更多應(yīng)用相關(guān)的規(guī)則, 增強(qiáng)了文本描述的抽象能力和表達(dá)能力. 
      
       5. 文本作為描述(description)而不是執(zhí)行指令(execution). C語言的源代碼與機(jī)器碼基本上是一一對應(yīng)的, 即源代碼本身所表達(dá)的就是指令的執(zhí)行過程. 而在Web應(yīng)用領(lǐng)域, HTML語言僅僅是聲明界面上需要展現(xiàn)什么, 但是如何去實(shí)現(xiàn)是由通用的解析引擎負(fù)責(zé),它并不是我們關(guān)注的重點(diǎn). 描述需要結(jié)合詮釋(解釋)才能夠產(chǎn)生實(shí)際的運(yùn)行效果, 才能對現(xiàn)實(shí)的物理世界產(chǎn)生影響.這在某種程度上實(shí)際上是延遲了執(zhí)行過程. 一種描述可以對應(yīng)多種詮釋, 例如同樣的元數(shù)據(jù)在前臺可以用來生成界面,在后臺可以用于生成數(shù)據(jù)庫, 進(jìn)行數(shù)據(jù)有效性校驗(yàn)等. 在特定的應(yīng)用領(lǐng)域中,執(zhí)行引擎可以是通用的, 可以獨(dú)立于描述本身不斷演化, 因此一種領(lǐng)域特定的描述,它所承載的內(nèi)容并不是固化的, 而是可以隨著執(zhí)行引擎的升級不斷增強(qiáng)的. 例如, 在Witrix平臺中, FlowDSL本身所做出的流程描述是穩(wěn)定的, 但是隨著流程引擎不斷改進(jìn),不斷引入新的功能,所有使用DSL的已實(shí)現(xiàn)的應(yīng)用都同步得到升級. http://canonical.javaeye.com/blog/275015
      
       6. Text = Process(Text) 這個(gè)不動點(diǎn)在Unix系統(tǒng)中得到了充分的應(yīng)用: 多個(gè)小程序通過管道(Pipe)組合在一起, 可以完成相當(dāng)復(fù)雜的功能. 一個(gè)更加豐富的處理模型是 XML = Process(XML). 文本描述很自然的支持多趟處理, 它使得我們可以充分利用全局知識(后續(xù)的處理過程可以使用前次處理過程收集的全局信息), 可以同時(shí)支持多個(gè)抽象層面(例如DSL的不斷動態(tài)展開). Witrix平臺中的編譯期運(yùn)行技術(shù)實(shí)際上就對應(yīng)于如下簡單規(guī)則: 編譯期運(yùn)行產(chǎn)生文本輸出, 對輸出文本再次進(jìn)行編譯. 通過這一遞歸模式, 可以簡單的實(shí)現(xiàn)動態(tài)解析與靜態(tài)描述之間的平衡. 模板(Template)技術(shù)是具有關(guān)鍵性作用的文本生成技術(shù). out.write("<div>");out.write(value);out.write("</div>");這種 API拼接方式顯然不如<div>${value}</div>這種模板生成方式直觀且易于使用. 在Witrix平臺的tpl模板語言中, xml的規(guī)范性使得在多趟編譯過程中我們一直可以維持某種形式約束.
      
       7. 不是所有的情況下都應(yīng)該使用文本. 普元EOS中所鼓吹的XML總線之類的技術(shù)是我所極力反對的. http://canonical.javaeye.com/blog/33794

    posted @ 2009-01-04 00:55 canonical 閱讀(1985) | 評論 (1)編輯 收藏

        代碼生成(Code Generation)本身是一個(gè)非常宏大的概念。從某種意義上說,當(dāng)我們明確了計(jì)算的意義之后,所做的一切都只是一系列代碼生成的過程,最終的目標(biāo)是生成某種可執(zhí)行的機(jī)器碼。對web程序員來說,代碼生成是最熟悉不過的了,每天我們所做的工作就是JSP=>Servlet=>HTML。不過,現(xiàn)在多數(shù)人腦海中的代碼生成,指的一般只是根據(jù)配置輸出一個(gè)或多個(gè)程序文本的過程,最常見的是根據(jù)數(shù)據(jù)庫模型生成增刪改查相關(guān)代碼。這種技術(shù)其實(shí)很少在小型以上的項(xiàng)目中起到積極的作用.因?yàn)橐话愕纳晒ぞ叨紱]有實(shí)現(xiàn)追加功能,無法適應(yīng)模型的增量修改。此外一般生成的代碼相比于手工書寫的代碼要更加冗長,需要被直接理解的代碼總量不降反升.為圖一時(shí)之快,所要付出的是長期的維護(hù)成本。

       在應(yīng)用開發(fā)中,有些領(lǐng)域是非常適合于使用代碼生成技術(shù)的。例如根據(jù)領(lǐng)域模型生成ORM(對象-關(guān)系映射)描述,或者根據(jù)接口描述生成遠(yuǎn)程調(diào)用代理/存根 (Proxy/Stub)等。因?yàn)樗鼈儗?shí)際上只是對同一信息的不同技術(shù)形式或者不同技術(shù)層面的同義反復(fù)而已。這種生成最理想的方式是動態(tài)進(jìn)行,可以隨時(shí)保持模型的有效性。RoR(RubyOnRails)框架中ActiveRecord技術(shù)便是一個(gè)成功的范例,它甚至提供了動態(tài)生成的DAO函數(shù),減少了一系列的包裝調(diào)用過程。

       代碼生成更加深刻的應(yīng)用是完成高層模型向低層模型的轉(zhuǎn)化,這一過程往往是非平凡(non-trivial)的。在Witrix平臺中通過代碼生成來支持領(lǐng)域抽象,可以用非常低的成本跨越結(jié)構(gòu)障礙,將自定義的領(lǐng)域模型嵌入到現(xiàn)有的技術(shù)體系中。這其中我們的主要工作是解決了生成代碼與手工書寫代碼之間的有效隔離及動態(tài)融合問題,確保代碼生成可以反復(fù)的以增量的方式進(jìn)行,同時(shí)支持最細(xì)粒度處對生成的代碼進(jìn)行定制調(diào)整。

       舉一個(gè)簡單的例子,假設(shè)現(xiàn)在需要開發(fā)一個(gè)三步審批的流程,每一步的操作人可以錄入意見,可以選擇通過或者回退,可以選擇下一步操作的具體操作人,系統(tǒng)自動記錄操作時(shí)間,每個(gè)操作人可以查看自己的操作歷史等。雖然在現(xiàn)有技術(shù)體系中實(shí)現(xiàn)這一功能需要不少代碼,但是在業(yè)務(wù)層面上描述這一功能并不需要很多文字,實(shí)際需要提供的信息量很小。顯然,建立領(lǐng)域模型是比較適合的做法,可以定義一種DSL(Domain Specific Language)來描述這一模型。

       <flow_cp:SeqFlow>
         
    <step id="draft" userField="draferId" dateField="draftTime" waitStatus="drafted" />
         
    <step id="check" userField="checkerId" dateField="checkTime" opinionField="checkOpinion"
                       waitStatus
    ="sent" />
         
    <step id="approve" userField="approverId" dateField="approveTime"
                    opinionField
    ="approveOpinion" waitStatus="checked" passStatus="approved" />  
       
    </flow_cp:SeqFlow>

       以上功能涉及到多個(gè)操作場景,實(shí)現(xiàn)的時(shí)候需要補(bǔ)充大量具體信息,其中很大一部分信息來自于背景知識,例如顯示樣式,界面布局,前后臺通信方式等。以上模型可以進(jìn)一步抽象為如下標(biāo)簽
       <flow_cp:StepFlow3/>

      在不同應(yīng)用中復(fù)用以上流程邏輯的時(shí)候可能需要局部修正,例如
       <flow_cp:StepFlow3>
          
    <step id="check" userField="checker" />
       
    </flow_cp:StepFlow3>

       更加復(fù)雜的情形是DSL本身提供的抽象無法滿足全部需求,而需要在局部補(bǔ)充更多模型之外的信息,例如物品接收單審批通過后自動導(dǎo)入庫存等。
     
       在Witrix中,代碼生成不是直接產(chǎn)生最終的輸出,而是在編譯期生成基礎(chǔ)模型,它與補(bǔ)充描述通過extends算子進(jìn)行融合運(yùn)算之后產(chǎn)生最終輸出, 這種融合可以實(shí)現(xiàn)基礎(chǔ)功能的新增,更改或者刪除。典型的調(diào)用形式為

      
    <biz-flow>
           
    <extends>
             
    <flow_cp:StepFlow3>
               
    <step id="check" userField="checker" />
              
    </flow_cp:StepFlow3>
           
    </extends>
           
           
    <action id="pass_approve">
             .
           
    </action>
       
    </biz-flow>

    這里的操作過程可以看作是BizFlow extends SeqFlow<FlowConfig extends StepFlow3Config>,與泛型技術(shù)非常類似,只是需要更強(qiáng)的局部結(jié)構(gòu)控制能力。
        按照級列理論http://canonical.javaeye.com/blog/33824 ,我們可以定義一個(gè)DSL的級列,整個(gè)抽象過程為
         Context0 + DSL1 + EXT0 = DSL0
         Context1 + DSL2 + EXT1 = DSL1
           

        在目前一些通用語言中,也有一些所謂內(nèi)嵌DSL的方案,可以提供比較簡潔的業(yè)務(wù)描述。但是僅僅建立DSL描述是不充分的,從級列理論的觀點(diǎn)看,我們必須提供一種DSL的補(bǔ)充手段,能夠在細(xì)節(jié)處補(bǔ)充DSL模型之外的信息,實(shí)現(xiàn)兩者的自然融合。同時(shí)我們應(yīng)該可以在不同的抽象層面上獨(dú)立的進(jìn)行操作,例如在 DSL1和DSL2的層面上都可以通過類似繼承的操作實(shí)現(xiàn)局部調(diào)整,這同時(shí)也包括在不同的抽象層面上都能對模型進(jìn)行合法性校驗(yàn)。
       

    posted @ 2008-11-23 11:57 canonical 閱讀(1919) | 評論 (0)編輯 收藏

       軟件系統(tǒng)的構(gòu)建之所以與建筑工程不同,無法達(dá)到建筑工程的精確性和可控性,其中一個(gè)很重要的原因在于建筑的產(chǎn)物是一個(gè)靜態(tài)的結(jié)構(gòu),建筑的過程主要是采用各種預(yù)制件填充某個(gè)規(guī)劃好的建筑空間,而軟件是一種動態(tài)運(yùn)行的產(chǎn)品,它的各個(gè)組成部分之間的關(guān)系不是可以靜態(tài)描述的,而是存在著復(fù)雜的交互關(guān)系,而且軟件在運(yùn)行的過程中還需要根據(jù)需求的變化進(jìn)行動態(tài)的調(diào)整,這種動態(tài)性使得軟件開發(fā)中很難抽象出固定的預(yù)制件,很難像建筑工程那樣實(shí)現(xiàn)標(biāo)準(zhǔn)件的組裝。現(xiàn)在所謂構(gòu)件技術(shù)的構(gòu)件插拔圖景其實(shí)是具有誤導(dǎo)性的。

        但是從另外一方面說,軟件內(nèi)在的動態(tài)性使得它可以具備更強(qiáng)的適應(yīng)能力,如果所編制的軟件把握住了業(yè)務(wù)機(jī)制的核心內(nèi)容,則在運(yùn)行過程中只需要進(jìn)行少量調(diào)整就可以應(yīng)對大量類似情況。100棟類似的建筑需要花費(fèi)100倍的建造費(fèi)用,而100個(gè)近似的軟件需求的滿足可能只需要花費(fèi)2至3倍的開發(fā)費(fèi)用。現(xiàn)代軟件企業(yè)在研發(fā)過程中都在不斷的追求自身產(chǎn)品的平臺化,其目的正在于以不斷提高的適應(yīng)性來應(yīng)對不斷變化的客戶需求。我們所面對的要求不僅僅是精確把握需求,而是要深刻把握需求背后所對應(yīng)的業(yè)務(wù)機(jī)制。

    posted @ 2008-09-01 23:24 canonical 閱讀(459) | 評論 (2)編輯 收藏

      AOP(Apsect Oriented Programming)概念的正式出現(xiàn)也有一些時(shí)日了,但是它在程序構(gòu)造過程中似乎仍未找到合適的切入點(diǎn),一般系統(tǒng)的設(shè)計(jì)實(shí)現(xiàn)很少將AOP作為必要的技術(shù)元素。AOP作為一種普適的技術(shù)思想,它所代表的是程序結(jié)構(gòu)空間中的定位和組裝技術(shù)。http://canonical.javaeye.com/blog/34941 AOP使我們可以通過非侵入性的方式動態(tài)修改“任意”已經(jīng)構(gòu)建好的程序,而不需要事前有大量的設(shè)計(jì)準(zhǔn)備。原則上說,這種技術(shù)思想是可以在任何程序語言基礎(chǔ)上進(jìn)行表達(dá)的,并不是只有java, C#這樣的面向?qū)ο笳Z言才允許AOP操作. Witrix平臺中所應(yīng)用的部分技術(shù)與AOP有些類似,只是大量的結(jié)構(gòu)調(diào)整表現(xiàn)為xml生成和xml變換,在具體的使用方式上也有一些微妙的差異。http://canonical.javaeye.com/blog/126467

      相對于通用程序語言,xml語言其實(shí)是AOP技術(shù)的一個(gè)更加合適的形式載體。
    1. xml格式特殊的規(guī)范性確保了在最細(xì)的邏輯粒度上,程序結(jié)構(gòu)也是可識別的,可操縱的(在這一點(diǎn)上非常類似于函數(shù)式語言)。而所有的命令式語言(imperative language)中,函數(shù)內(nèi)部的結(jié)構(gòu)都是很難采用統(tǒng)一方式進(jìn)行描述和定位的。
       <ns1:loop>
         
    <rpt:Row/>
       
    </ns1:loop>

    2. xml節(jié)點(diǎn)的坐標(biāo)可以采用xpath或者css選擇符等通用方式進(jìn)行描述,而一般程序結(jié)構(gòu)無法達(dá)到xml格式這樣的均一性,其中的坐標(biāo)定位方式要復(fù)雜得多。
    3. xml節(jié)點(diǎn)上可以增加任意屬性,不同的屬性可以屬于不同的命名空間(namespace),這些屬性可以輔助AOP的定位機(jī)制。而一般程序語言中如果沒有Annotation機(jī)制, 則定位只能依賴于函數(shù)名和類名(函數(shù)參數(shù)只有類型沒有名稱),而類名和函數(shù)名隨時(shí)可能因?yàn)闃I(yè)務(wù)變化而調(diào)整(不是專為定位而存在), 由此構(gòu)建的切點(diǎn)描述符是不穩(wěn)定的。
     
    <ui:PageTable pager="${pager}" cache:timeout="1000" />
    4. xml節(jié)點(diǎn)的增刪改查顯然要比字節(jié)碼生成技術(shù)要簡單和直觀得多。
       

       AOP技術(shù)難以找到應(yīng)用的一個(gè)重要原因在于很多人機(jī)械式的將它定位為一種橫切技術(shù),認(rèn)為它的價(jià)值完全在于某個(gè)確定的切面可以插入到多個(gè)不同的切點(diǎn),實(shí)現(xiàn)系統(tǒng)的橫向分解。而在實(shí)際應(yīng)用中,業(yè)務(wù)層面上很少具有可抽象的固定的共同性,我們所迫切需要的一般是對已有程序結(jié)構(gòu)進(jìn)行動態(tài)擴(kuò)展的一種能力。橫切是AOP的一種特殊的應(yīng)用,但不是它的全部。相對于繼承(inheritance)等依賴于概念詮釋的結(jié)構(gòu)擴(kuò)展機(jī)制,AOP所代表正是對程序結(jié)構(gòu)空間進(jìn)行任意操縱的一種能力。AOP可以為基礎(chǔ)結(jié)構(gòu)增加功能,改變原有功能實(shí)現(xiàn),也可以取消原有功能實(shí)現(xiàn),它不需要把所有的擴(kuò)展邏輯按照樹形結(jié)構(gòu)進(jìn)行組織,不要求在基礎(chǔ)結(jié)構(gòu)中為擴(kuò)展編寫特殊的代碼。這種自由的結(jié)構(gòu)擴(kuò)展能力在Witrix平臺中被發(fā)展為“實(shí)現(xiàn)業(yè)務(wù)代碼與平臺基礎(chǔ)架構(gòu)之間的動態(tài)融合”。

       在Witrix平臺的實(shí)際應(yīng)用中,AOP的切點(diǎn)匹配能力并不是十分重要。一般情況下我們主要通過整體結(jié)構(gòu)規(guī)劃來確保控制點(diǎn)意義明確且相對集中,因此不需要額外通過切點(diǎn)匹配進(jìn)行業(yè)務(wù)功能的再組織,不需要再次從雜亂的程序邏輯中重新發(fā)現(xiàn)特殊的控制點(diǎn)。例如在Witrix平臺的Jsplet框架中所有后臺事件響應(yīng)都通過objectName和objectEvent參數(shù)觸發(fā),在觸發(fā)后臺事件響應(yīng)函數(shù)之前都會調(diào)用bizflow文件中的beforeAction段。
       在bizflow文件中,aop操作是明確指定到具體函數(shù)的,使用模糊匹配在一般情況下只會使問題變得不必要的復(fù)雜化。例如擴(kuò)展actQuery函數(shù)
       <action id="aop-Query-default">
        
    <source>
           通過自定義標(biāo)簽抽象出多個(gè)Action之間的共用代碼
          
    <app:DoWorkA/>
        
    </source>
      
    </action>

       
       在Witrix平臺中結(jié)構(gòu)組裝主要是通過自定義標(biāo)簽庫和extends算子來實(shí)現(xiàn),它們都依賴于xml格式的規(guī)范性。
    1. 通過在custom目錄下實(shí)現(xiàn)同名的自定義標(biāo)簽,即可覆蓋Witrix平臺所提供的缺省標(biāo)簽實(shí)現(xiàn),這里所依賴的并不是復(fù)雜的匹配過程,而是自然直觀的映射過程。http://canonical.javaeye.com/blog/196826
    2. 所有的xml配置文件支持extends操作,它允許定制兩個(gè)具有業(yè)務(wù)含義的xml節(jié)點(diǎn)之間的結(jié)構(gòu)融合規(guī)則。例如<biz-flow extends="docflow">。

       實(shí)際使用中, AOP技術(shù)的一個(gè)應(yīng)用難點(diǎn)在于狀態(tài)空間的管理問題。一般interceptor中所能訪問的變量局限為this指針?biāo)鶖y帶的成員變量,以及函數(shù)調(diào)用時(shí)傳入的調(diào)用參數(shù)。interceptor很難在狀態(tài)空間中創(chuàng)建新的變量,也很難讀取在其他地方所產(chǎn)生的狀態(tài)變量。例如對于如下擴(kuò)展 A(arg1); B(arg2); C(arg3); =〉 Ax(arg1); B(arg2); Cx(arg3); 因?yàn)樵械恼{(diào)用序列中沒有傳遞額外的參數(shù),因此A和C的擴(kuò)展函數(shù)之間很難實(shí)現(xiàn)共享內(nèi)部變量x。在TPL模板語言中,tpl本身是無狀態(tài)的,狀態(tài)變量通過外部的$thisContext對象統(tǒng)一管理。通過這種行為與狀態(tài)的分離,結(jié)合靈活的變量作用域控制機(jī)制,可以以比較簡單的方式實(shí)現(xiàn)擴(kuò)展函數(shù)之間的信息共享。

    posted @ 2008-07-07 00:12 canonical 閱讀(1683) | 評論 (0)編輯 收藏

    主站蜘蛛池模板: 热re99久久6国产精品免费| 亚洲精品第一国产综合境外资源| 亚洲成a人片在线观看无码专区 | 亚洲精品无码久久久久APP| 16女性下面扒开无遮挡免费| 久久亚洲国产午夜精品理论片| 又硬又粗又长又爽免费看 | 中文字幕不卡亚洲| 美女视频黄.免费网址| 麻豆精品国产免费观看| 亚洲 欧洲 自拍 另类 校园| 2021久久精品免费观看| 亚洲福利一区二区| 在线日本高清免费不卡| 亚洲成年轻人电影网站www| 精品成人免费自拍视频| 亚洲国产精品嫩草影院在线观看 | 337p日本欧洲亚洲大胆精品555588| a毛片免费播放全部完整| 伊人久久大香线蕉亚洲五月天 | 97精品免费视频| 亚洲成年人在线观看| 久久久久久久99精品免费 | 免费看成人AA片无码视频吃奶| 亚洲精品V欧洲精品V日韩精品| 久久九九久精品国产免费直播| 亚洲综合色在线观看亚洲| h视频免费高清在线观看| 久久亚洲AV无码西西人体| 国产性生大片免费观看性| 亚洲综合伊人久久大杳蕉| 久久www免费人成看国产片| 夜夜春亚洲嫩草影院| 精品国产污污免费网站| 亚洲国产香蕉碰碰人人| 97在线视频免费| 91丁香亚洲综合社区| 午夜无遮挡羞羞漫画免费| 校园亚洲春色另类小说合集| 免费又黄又爽的视频| 亚洲第一视频在线观看免费|