關(guān)于面向構(gòu)件的一些思考
一、xml和元數(shù)據(jù)解決了接口脆弱性問題嗎?
其實面向構(gòu)件的基本理念和傳統(tǒng)的模塊、對象并沒有大的區(qū)別,我們的目標(biāo)仍然是把“大問題”分解成“小問題”來解決。那我們?yōu)槭裁葱枰嫦驑?gòu)件呢?關(guān)鍵是我們要重用對“小問題”的解決,來解決新的“大問題”。
傳統(tǒng)的問題分解手段為什么在重用上有問題呢?關(guān)鍵在于黃柳青博士說說的“接口脆弱性”問題:在新的上下文之下,同一個“小問題”的解決往往略有一點(diǎn)差異。就是這點(diǎn)差異,我們沒有辦法直接重用原來的模塊,必需“略微”改變一下接口,添加一個新的參數(shù),這樣這個模塊往往就和原來的模塊無法兼容了(如果想保持兼容,就要修改對原來的模塊的引用,這又帶來了新的問題)。下次我們再想重用的時候,也許又需要添點(diǎn)不同的玩意兒了,如果上次添的玩意兒還有用,就在上次的的基礎(chǔ)上改,不然就在舊的模塊上面改……
那么,標(biāo)簽數(shù)據(jù)和xml真的能解決我們遇到的問題嗎?我們假設(shè)一個場景來看看:
首先,我做一個模塊來計算個人所得稅,接口很簡單,接受一個金額,按照預(yù)定的公式計算出所得稅額,輸出計算出來的金額。在第一個產(chǎn)品里面這個模塊工作的很好。
接著,有一個新的產(chǎn)品也需要計算個人所得稅,那么理所當(dāng)然我們會重用這個模塊,不過這次的需求有點(diǎn)不同,我的新客戶的雇員里面有外籍雇員,對外籍雇員我們的計算方式有點(diǎn)不同。那么現(xiàn)在接口需要添加一個國籍字段。
又來一個單子。我們這個新客戶也有外籍雇員,而且這些外籍雇員還有不同的簽證類型,臨時工作簽證和長期工作簽證的稅率又有不同,這次我們需要添加一個簽證類型字段……
如果我們用傳統(tǒng)的接口,那么我們一開始會這樣寫接口:
currency incomeTax(currency salary)
第二次則是
currency incomeTax(currency salary, Nationality nationality)
第二次則是
currency incomeTax(currency salary, Nationality nationality, VisaType visaType)
很顯然,我們在重用的時候會遇到麻煩。
換種方式寫接口,也許會有幫助
第一次:
currency incomeTax(Parameter parameter)
第二次:
currency incomeTax(Parameter parameter)
第三次:
currency incomeTax(Parameter parameter)
通過Parameter的多態(tài),我們可以保持住接口。在函數(shù)里面我們可以判斷Parameter的類型來決定怎么計算。這樣做有兩個主要問題,一是我們需要多維護(hù)一組Parameter類,二是有人說如果我們試圖在計算的時候先判斷參數(shù)類型的話,先賞自己兩耳光。
Ok,看來我們還需要更寬松的接口,那么還可以這樣
第一次:
currency incomeTax(Map parameter)
第二次:
currency incomeTax(Map parameter)
第三次:
currency incomeTax(Map parameter)
最后如果用xml,我們會得到:
第一次:
currency incomeTax(String parameterXML)
第二次:
currency incomeTax(String parameterXML)
第三次:
currency incomeTax(String parameterXML)
我們可以看到,這個接口和Map接口實質(zhì)上沒有區(qū)別,一個可以做到的原則上另一個也可以,但是使用上有兩點(diǎn)區(qū)別:一是Map容器里面我們可以放任意的對象,而用xml的話我們要用String來表示一切,因此還要在調(diào)用過程中進(jìn)行編碼、解碼(想像一下拿一部電影做base64編碼吧);二是即使傳遞的都是簡單的串,Map容器里面的數(shù)據(jù)可以快速的引用,而xml的數(shù)據(jù)我們要分析xml來獲取(更不要說java在解析xml方面確實不占優(yōu)勢,剛聽說java在一個測試中敗給.net就是被xml性能低下所拖累)。
我們再看看接口脆弱性是不是真的被解決了。不錯,在一致的接口之下,我們幾乎始終都可以進(jìn)行有效的調(diào)用,在處理接口參數(shù)的時候自適應(yīng)各種數(shù)據(jù),用默認(rèn)值代替缺少的參數(shù)。但是這樣做的代價就是我們失去了編譯期參數(shù)檢查的好處,并且在接口發(fā)生變化的時候沒有辦法(至少我想不出辦法)提醒接口的潛在使用者。還是用前面的例子。在我們添加“國籍”這個參數(shù)的時候,用傳統(tǒng)的方式,函數(shù)的調(diào)用者會知道,但是不管是用map還是用xml,我們只能在沒有接收到nationality參數(shù)的時候使用默認(rèn)值Chinese,但是可以想像很多開發(fā)人員并不知道計算所得稅的時候還要知道國籍。我們怎么防止模塊的使用者在傳遞一個外國雇員的數(shù)據(jù)時忘了傳遞國籍呢?我能想得出的最好的辦法也只是把這個問題列入潛在問題清單里面,在code review的時候人工檢查。
嚴(yán)格的接口定義,可以讓機(jī)器幫助我們維持我們的正確性,但是犧牲的是靈活性和可重用性。寬松的接口在帶來靈活性和可重用性的同時,付出的代價是犯錯誤的可能性和排錯的困難性。
使用xml的額外的好處是,我們可以很容易的在不同的平臺和語言之間交互,但是至少在現(xiàn)在,我看不到這個優(yōu)勢能有任何體現(xiàn):EOS好像完全是java開發(fā)的。
因此,我看不出xml+元數(shù)據(jù)在解決接口脆弱性上的銀彈般的神奇共用,在我看來,它甚至不如相對比較傳統(tǒng)的Map接口。
很遺憾的是,關(guān)于EOS的“總線”概念我現(xiàn)在沒有辦法搜集的足夠的資料,我很不理解xml數(shù)據(jù)是在什么樣的一條“總線”上流動的,而Map形式的數(shù)據(jù)包在這樣一條“總線”上是否能夠發(fā)揮相同的作用 。