|
? 程序中的各種類(lèi)(Class),包(package)等首先體現(xiàn)的是架構(gòu)設(shè)計(jì)中的一種概念分布. 一個(gè)良好的設(shè)計(jì)相當(dāng)于是建立一個(gè)結(jié)構(gòu)合理的概念框架, 隨著系統(tǒng)的不斷發(fā)展, 作為概念載體的類(lèi)(Class)不斷吸收相關(guān)的實(shí)現(xiàn), 從而使其外延不斷豐富起來(lái), 而其內(nèi)涵也愈加變得明晰. 系統(tǒng)中概念的分化, 最顯著的不是業(yè)務(wù)模塊的劃分, 而是技術(shù)層面與業(yè)務(wù)層面的分離. 因?yàn)榧夹g(shù)手段與業(yè)務(wù)在很大程度上是相互獨(dú)立的, 因?yàn)?[無(wú)論]實(shí)現(xiàn)什么樣的業(yè)務(wù), 我們[都]將用到某種技術(shù)手段.? 而當(dāng)我們可以回答一個(gè)"無(wú)論..都" 的問(wèn)題的時(shí)候, 它意味著某個(gè)概念可以容納眾多變化, 而它自然有資格成為某種獨(dú)立的部分.? ? 作為技術(shù)層面概念聚集的例子, 我們可以看一下spring framework中的JdbcTemplate類(lèi), 這個(gè)類(lèi)在spring的概念體系中對(duì)應(yīng)于"Jdbc調(diào)用幫助類(lèi)"這一概念, 它的目的是幫助我們盡量通過(guò)一次函數(shù)調(diào)用得到我們所要的結(jié)果, 但是我已經(jīng)不止一次的看到很多人使用如下調(diào)用 ?? List results = jdbcTemplate.query(...); ?? List ret = new ArrayList(); ?? for(int i=0;i<results.size();i++){ ???? ret.add(((Map)results.get(i)).get("someField")); ?? } 這段代碼的目的是為了得到某一列的值, 而JdbcTemplate類(lèi)沒(méi)有直接提供這一函數(shù). 為了不等待spring的升級(jí), 顯然我們需要建立一個(gè)JdbcTemplate的擴(kuò)展類(lèi), 它直接提供一個(gè)queryScalarList函數(shù), 而不是讓這種純粹技術(shù)性的循環(huán)語(yǔ)句散見(jiàn)在程序代碼的各個(gè)角落. ? 告別裸奔編程是我對(duì)同事的基本要求之一. 即使是考慮最細(xì)致的軟件組件, 它也難以保證能夠預(yù)想到所有的變化形式, 而在系統(tǒng)中集成一些第三方組件的時(shí)候, 一般總要加入一些特定的假設(shè), 此時(shí)也需要一個(gè)技術(shù)隔離層. 例如在頁(yè)面開(kāi)發(fā)中, 我們強(qiáng)制使用witrix平臺(tái)定義的js.Ajax對(duì)象, 而不是prototype.js中原始提供的Ajax.Updater等對(duì)象. 在應(yīng)用一段時(shí)間之后, js.Ajax對(duì)象上聚集了一系列與ajax相關(guān)的調(diào)用指令.
? hibernate的應(yīng)用中一般總是將entity映射為強(qiáng)類(lèi)型的java類(lèi),這為程序操縱帶來(lái)很多便利,同時(shí)可以將大量動(dòng)態(tài)過(guò)程隱蔽在對(duì)象包絡(luò)之下。映射為java類(lèi)的一個(gè)主要問(wèn)題在于無(wú)法在程序運(yùn)行時(shí)刻對(duì)于程序進(jìn)行修改,而數(shù)據(jù)結(jié)構(gòu)的局部修改幾乎是無(wú)法避免的。hibernate3本身支持動(dòng)態(tài)數(shù)據(jù)模型,它允許我們把entity映射為Map數(shù)據(jù)類(lèi)型, 當(dāng)數(shù)據(jù)結(jié)構(gòu)發(fā)生變化的時(shí)候, 只需要修改hbm文件即可改變映射模型,而不需要修改java實(shí)體類(lèi)代碼.? ? ? 在hbm定義文件中,如果我們不指定name屬性,而是指定entity-name屬性,則我們可以將entity映射為Map, 而不是一個(gè)java實(shí)體類(lèi). ? <class ??? entity-name="test.DynamicEntity" ??? table="DYNAMIC_ENTITY" ? >...</class> ? 此外, 也可以選擇將部分字段動(dòng)態(tài)映射到Map ? <class ...> ??? <dynamic-component name="dynamicAttributes"> ????? <property name="foo" column="FOO"/> ????? <property name="bar" column="BAR"/> ??? </dynamic-component> ? </class> 在HQL語(yǔ)句中可以直接使用o.dynamicAttributes.foo來(lái)訪問(wèn)foo屬性,所有操作與普通屬性相同。 ? 為了實(shí)現(xiàn)hiberante映射模型的動(dòng)態(tài)更新,我們首先需要實(shí)現(xiàn)sessionFactory的動(dòng)態(tài)更新。目前hibernate的實(shí)現(xiàn)只允許從hbm文件重建sessionFactory, 即新建一個(gè)sessionFactory替換原有的sessionFactory, 在使用spring的情況下,這需要對(duì)org.springframework.orm.hibernate3.LocalSessionFactoryBean進(jìn)行小小的hack。 ? 為了將動(dòng)態(tài)屬性和普通屬性同樣對(duì)待,要求在操作實(shí)體對(duì)象屬性的時(shí)候需要能夠自動(dòng)處理nested property, 即需要如下類(lèi)似的方法:entityDao.getProperty(entity,"dynamicAttributes.foo"), entityDao.setProperty(entity,"dynamicAttributes.foo", attrValue). ? 為了使得應(yīng)用程序自動(dòng)處理新增屬性,要求程序是meta驅(qū)動(dòng)的:當(dāng)實(shí)體對(duì)象增加了一個(gè)屬性時(shí),只需要修改meta文件,即可完成對(duì)于該屬性的增刪改查匯總等普通應(yīng)用需求。
? 最近ruby語(yǔ)言的流行似乎再次引發(fā)了DSL(Domain Specific Language)討論的熱潮。從語(yǔ)法表現(xiàn)形式上看,通過(guò)對(duì)于ruby語(yǔ)言的深度hack, 充分挖掘ruby語(yǔ)言的某些語(yǔ)法特征,可以使得正常的ruby語(yǔ)句看起來(lái)比其他計(jì)算機(jī)語(yǔ)言更接近于人類(lèi)的自然語(yǔ)言,某些人因此認(rèn)定ruby語(yǔ)言是DSL的天然載體。但是在我看來(lái),具體語(yǔ)言的語(yǔ)法表達(dá)形式對(duì)于DSL的核心價(jià)值而言并不是最關(guān)鍵的。 ?? 首先,DSL的核心在于高效的表達(dá)語(yǔ)義,而并不在于是否接近自然語(yǔ)言。接近于自然語(yǔ)言并不意味著更加domain, 因?yàn)樽匀徽Z(yǔ)言也是一種通用語(yǔ)言,它未必能夠比采用其他語(yǔ)法形式的語(yǔ)言更加有效的對(duì)domain事物進(jìn)行描述。典型的有數(shù)學(xué)符號(hào)和化學(xué)分子式。 ?? 第二,作為DSL, 緊湊的表達(dá)形式是一方面,另一方面是這種表達(dá)形式的穩(wěn)定性,即如何防止人們寫(xiě)出不符合DSL規(guī)范的語(yǔ)句。ruby語(yǔ)言的片斷直接作為DSL無(wú)疑是一種naive的解決方案,我們可以輕易寫(xiě)出大量不同形式的ruby語(yǔ)句,而它們?cè)谡Z(yǔ)義上是等價(jià)的(這意味著通過(guò)單元測(cè)試也無(wú)法發(fā)現(xiàn)它們的不同),即人們不按照設(shè)計(jì)的DSL語(yǔ)法書(shū)寫(xiě),這造成DSL的解體。 ?? 作為一種DSL構(gòu)造語(yǔ)言,其核心能力在于如何將second class的domain中的概念(非語(yǔ)言本身內(nèi)置的概念)封裝到first class的表達(dá)形式中。ruby作為一種動(dòng)態(tài)語(yǔ)言,可以更加輕易對(duì)于自身meta data進(jìn)行內(nèi)省,典型的如ruby中的ActiveRecord設(shè)計(jì). 但是在我看來(lái),這種概念提升能力在ruby的語(yǔ)法結(jié)構(gòu)中也是有限的,原因恰在于ruby的語(yǔ)法太多樣化了。實(shí)際上,我更加看好xml結(jié)構(gòu)的均一性。
? CRUD(Create Read Update Delete)是一般應(yīng)用程序中最基礎(chǔ)的操作,但是用戶的需求卻很難直接映射到CRUD操作上。例如常見(jiàn)的需求如下: ?1. 不同的業(yè)務(wù)處理處于不同狀態(tài)的業(yè)務(wù)對(duì)象: ???? 業(yè)務(wù)A處理狀態(tài)為X的業(yè)務(wù)對(duì)象,而業(yè)務(wù)B處理狀態(tài)為Y的業(yè)務(wù)對(duì)象 ?2. 業(yè)務(wù)對(duì)象處于不同狀態(tài)的時(shí)候允許的操作不同: ??? 狀態(tài)處于X的業(yè)務(wù)對(duì)象允許操作U, 而狀態(tài)處于Y的業(yè)務(wù)對(duì)象允許操作V ?3. 不同的業(yè)務(wù)操作可能修改業(yè)務(wù)對(duì)象的不同屬性: ???? 操作U修改業(yè)務(wù)對(duì)象的屬性P, 操作V修改業(yè)務(wù)對(duì)象的屬性Q ?4. 具有不同權(quán)限的人能夠從事的業(yè)務(wù)不同: ????? 角色R處理業(yè)務(wù)A, 角色S處理業(yè)務(wù)B ?5. 具有不同權(quán)限的人即使從事同一業(yè)務(wù),所能夠操作的業(yè)務(wù)對(duì)象集合也不同: ???? 角色R處理部門(mén)M的業(yè)務(wù)對(duì)象,角色S處理部門(mén)N的業(yè)務(wù)對(duì)象. ?6. 具有不同權(quán)限的人即使可以操作同一業(yè)務(wù)對(duì)象,所能夠采取的業(yè)務(wù)操作也不同: ????? 角色R只能進(jìn)行操作U, 角色S只能進(jìn)行操作V ?7. 在業(yè)務(wù)對(duì)象上執(zhí)行操作之后可能造成狀態(tài)變遷: ????? 處于狀態(tài)X的業(yè)務(wù)對(duì)象上執(zhí)行操作U后狀態(tài)變?yōu)閅
以上這些需求往往是系統(tǒng)中最易變的部分, 而它們?cè)诟拍钌锨∏”憩F(xiàn)為對(duì)CRUD的一種限制性描述. 因此通過(guò)如下擴(kuò)展我們可以定義BizFlow的概念: BizFlow = CRUD + Filter. 根據(jù)這種觀念, witrix平臺(tái)中BizFlow被實(shí)現(xiàn)為DaoWebAction的一種無(wú)縫擴(kuò)展. ?? 在jsplet框架中我們通過(guò)如下url模式來(lái)訪問(wèn)后臺(tái)的CRUD操作: ?? /list.jsp?objectName=MyObj&objectEvent=Query 為了實(shí)現(xiàn)BizFlow只需通過(guò)spring為DaoWebAction配置一個(gè)xml配置文件, 此后仍然可以通過(guò) ??? /list.jsp?objectName=MyObj&objectEvent=Query 來(lái)訪問(wèn)后臺(tái)的CRUD操作,只是后臺(tái)會(huì)自動(dòng)應(yīng)用配置文件中的 bizId="default", bizActionId="Query-default"等配置項(xiàng). 如果我們采用如下url來(lái)訪問(wèn) ??? /list.jsp?objectName=MyObj&objectEvent=Query&$bizId=test&$bizActionId=test??? 則后臺(tái)將應(yīng)用配置項(xiàng) bizId=manage, bizActionId=Query-test, 而 ??? /list.jsp?objectName=MyObj&objectEvent=BizAction&$bizId=test&$bizActionId=test??? 則對(duì)應(yīng)于配置項(xiàng) bizId=manage, bizActionId=BizAction-test. ?? 應(yīng)用BizFlow配置項(xiàng)之后,所有前臺(tái)代碼都可以不做出任何改變, 因?yàn)樗鼈冎皇菍?duì)于給定數(shù)據(jù)的展現(xiàn). ?? ?? BizFlow可以看作是CRUD加上簡(jiǎn)單的流程控制和權(quán)限控制所構(gòu)成, 但是它與完整的工作流模型還是有著顯著區(qū)別的. 工作流中所關(guān)注的重點(diǎn)首先是流程實(shí)例而不是業(yè)務(wù)對(duì)象實(shí)例, 在一個(gè)流程中是否存在唯一的業(yè)務(wù)對(duì)象,以及業(yè)務(wù)對(duì)象的狀態(tài)是否隨著流程流轉(zhuǎn)發(fā)生變化完全是一件獨(dú)立的事情,它們并不屬于抽象的工作流模型本身. 理論上說(shuō),一個(gè)業(yè)務(wù)對(duì)象可以同時(shí)參與多個(gè)流程. 在工作流建模中主要通過(guò)流程步驟的先后順序的約束來(lái)描述業(yè)務(wù)進(jìn)程, 處于同一狀態(tài)的業(yè)務(wù)對(duì)象可能處在不同的流程步驟中. 而B(niǎo)izFlow可以看作是狀態(tài)驅(qū)動(dòng)的, 當(dāng)前業(yè)務(wù)步驟直接由業(yè)務(wù)對(duì)象的狀態(tài)決定. 在BizFlow中因?yàn)橐暯鞘菢I(yè)務(wù)對(duì)象的狀態(tài),因此我們直接面對(duì)的是大量處于同一狀態(tài)的不同的業(yè)務(wù)處理過(guò)程, 而workflow中往往建模的時(shí)候強(qiáng)調(diào)單流程實(shí)例視角,而一般缺乏對(duì)于流程實(shí)例相關(guān)性的描述. 現(xiàn)在國(guó)內(nèi)很多人認(rèn)為工作流就是狀態(tài)機(jī)其實(shí)是對(duì)workflow概念的一種誤讀. ?
? 在國(guó)內(nèi)做項(xiàng)目,客戶需求控制總是個(gè)令人頭痛的問(wèn)題。很多時(shí)候問(wèn)題在于用戶并不知道確定自己需要什么,他只是就著目前系統(tǒng)的狀況提出一些他可以設(shè)想到的情形, 但最終他可能并不真的使用這些功能,或者他的想法會(huì)發(fā)生其他的變化。所以如何引導(dǎo)客戶走到我們所謂的"正確"方向上來(lái)是一個(gè)非常重要而復(fù)雜的問(wèn)題。 ? 昨天一個(gè)同事就此事做了個(gè)有趣的比喻??蛻舻男枨笫浅燥栵垼憬o他一個(gè)饅頭,他和你爭(zhēng)論饅頭是大點(diǎn)好還是小點(diǎn)好。但是如果你給客戶一個(gè)包子,情況就完全不同了,客戶關(guān)注的重點(diǎn)肯定是包子是什么餡,豬肉三鮮還是韭菜雞蛋。。。很多人還有忌口的。從此沒(méi)完沒(méi)了。
? http://code.google.com/webtoolkit/? 最近google發(fā)布了Google Web Toolkit(GWT)開(kāi)發(fā)包,這是一種使用java語(yǔ)言開(kāi)發(fā)AJAX應(yīng)用的開(kāi)發(fā)框架。從技術(shù)上看,GWT并沒(méi)有什么新鮮之處,類(lèi)似的概念在多年之前就已經(jīng)有各種嘗試了,這些嘗試從未真正吸引到足夠的注意。GWT的優(yōu)勢(shì)也許在于提供了一套模擬工具,另外可能在屏蔽browser的兼容性和bug方面做得更好一些,但是真正的技術(shù)思想并沒(méi)有什么突破. Ruby On Rails同樣是試圖將ruby語(yǔ)言直接映射到前臺(tái)程序, 但是它通過(guò)一個(gè)通用的prototype.js庫(kù)最小化了ruby語(yǔ)言和js語(yǔ)言之間的區(qū)別,在概念上要比GWT的java2js的compiler概念要更加新穎一些. ( http://mir.aculo.us/stuff/COR_20060413_RailsAjax.pdf) ? 對(duì)于web開(kāi)發(fā)而言,我總認(rèn)為要發(fā)揮web的特色,而不是把它約束到其他領(lǐng)域的開(kāi)發(fā)模式上。js+dom+html文本所能做到的結(jié)構(gòu)控制程度要遠(yuǎn)遠(yuǎn)超越組件技術(shù),我也從未發(fā)現(xiàn)學(xué)習(xí)java要比學(xué)習(xí)html要更加容易。也許對(duì)于某些對(duì)于web一無(wú)所知的java開(kāi)發(fā)人員來(lái)說(shuō),GWT有些意義,也許GWT會(huì)特別適合于某些特定的領(lǐng)域,但是作為一種通用的開(kāi)發(fā)框架,我并不看好它。
? 傳統(tǒng)的Mode2模式的服務(wù)器端框架在處理AJAX應(yīng)用的時(shí)候存在一定的不適應(yīng)性,這主要的原因在于Model2基于推模式,它隱含的假設(shè)是基于action的處理結(jié)果生成整個(gè)頁(yè)面,而AJAX應(yīng)用中所強(qiáng)調(diào)的是頁(yè)面局部的變化,只更新發(fā)生變化的部分,而不是重新生成整個(gè)頁(yè)面(change instead of create), 這兩者之間存在著內(nèi)在的不協(xié)調(diào)。有些人推崇后臺(tái)服務(wù)程序只返回xml數(shù)據(jù)的方法,將顯示層完全推到前臺(tái)。雖然在前臺(tái)通過(guò)js腳本操縱DOM節(jié)點(diǎn)可以實(shí)現(xiàn)非常細(xì)粒度上的控制,但是我們并不總是需要最細(xì)粒度上的控制權(quán)的。例如現(xiàn)在我們?cè)谇芭_(tái)實(shí)現(xiàn)一個(gè)grid控件, grid控件本身只需要控制到單元格層次即可,而不需要對(duì)于單元格里存放什么內(nèi)容有預(yù)先的假設(shè). grid.getCell(i,j).innerHTML = cellHtml是非常自然的一種解決方式。完全通過(guò)dom來(lái)構(gòu)造界面面臨著眾多問(wèn)題,除了瀏覽器bug這種揮之不去的噩夢(mèng)之外,在實(shí)現(xiàn)過(guò)程中我們往往會(huì)引入對(duì)界面元素的大量限制條件,而無(wú)法做到集成各種來(lái)源的控件。 ? 在服務(wù)器端生成頁(yè)面片斷的方式也稱(chēng)為AJAH,表面上看起來(lái)它比AJAX要簡(jiǎn)易一些,是很多服務(wù)器端框架引入AJAX概念的鄉(xiāng)間小徑。但有趣的是在基于拉模式(pull mode)的服務(wù)器端MVC框架中,AJAH是在架構(gòu)上比AJAX更加靈活的一種方式。在witrix平臺(tái)的jsplet框架中,web訪問(wèn)的基本形式如下: ?? /view.jsp?objectName=XXObject&objectEvent=XXEvent&otherArgs&tplPart=XXPart 其中objectName對(duì)應(yīng)于后臺(tái)的服務(wù)對(duì)象,objectEvent參數(shù)映射到服務(wù)對(duì)象的方法,view.jsp是對(duì)于后臺(tái)對(duì)象進(jìn)行渲染的模板頁(yè)面,而tplPart參數(shù)可以指定只使用模板的某一部分進(jìn)行渲染。如果我們選擇json.jsp或者burlap.jsp作為渲染模板,則可以退化到返回?cái)?shù)據(jù)而不是內(nèi)容的方式。在js中進(jìn)行簡(jiǎn)單的封裝后我們可以通過(guò)如下方式進(jìn)行遠(yuǎn)程調(diào)用: ? new js.Ajax().setObjectName("XXObject").setObjectEvent("XXEvent").addForm("XXFormId").callRemote(callbackFunc); ?? 它對(duì)應(yīng)的url請(qǐng)求為 ?? /json.jsp?objectName=XXObject&objectEvent=XXEvent&... 對(duì)于同樣的后臺(tái)業(yè)務(wù)處理,我們可以自由的選擇渲染模板,則可以很自然的得到更多的處理方式,例如返回javascript代碼來(lái)實(shí)現(xiàn)對(duì)于前臺(tái)的回調(diào)。
http://xp.c2.com/OnceAndOnlyOnce.html
http://c2.com/cgi/wiki?DontRepeatYourself
???? OAOO(Once And Only Once)是我們?cè)谲浖_(kāi)發(fā)中需要關(guān)注的基本原則之一. 唯一性當(dāng)然是一個(gè)值得追求的目標(biāo). 從正交分解的角度上說(shuō),系統(tǒng)可以由少數(shù)的正交基通過(guò)組合構(gòu)造出來(lái)。尤其在分析階段,我們需要牢牢把握住系統(tǒng)內(nèi)核的幾個(gè)變化維度。但是這并不意味著我們最終能夠做到每種可以想見(jiàn)的軟件元素都是唯一的,也不意味著保持唯一性永遠(yuǎn)都是最好的。 ???? 唯一性在軟件中最直接的體現(xiàn)就是代碼的重用(reuse), 除了實(shí)現(xiàn)起來(lái)節(jié)省了工作量之外,代碼重用的另一個(gè)作用在于維護(hù)了系統(tǒng)中概念的唯一性,或者更廣泛的說(shuō),它維護(hù)了系統(tǒng)中知識(shí)的唯一性。例如,如果我們經(jīng)常用到圓周率Pi,我們可以選擇在各處都直接寫(xiě)3.1415926, 也可以選擇定義一個(gè)系統(tǒng)常數(shù)PI, 在使用的時(shí)候引用這個(gè)常數(shù),保持關(guān)于PI值的知識(shí)的唯一性。其實(shí)只要各處的PI值是相同的,甚至只要是在誤差范圍內(nèi)相互匹配的(例如有些地方用3.14, 有些地方用3.1415926),程序就可以正確無(wú)誤的運(yùn)行,這樣就達(dá)到了我們開(kāi)發(fā)程序的目的,并不需要什么常數(shù)定義。只是為了保證這種知識(shí)的一致性,定義一個(gè)常數(shù)無(wú)疑是最簡(jiǎn)單直接的一種方法。從理論上說(shuō),我們實(shí)際需要的只是知識(shí)在軟件中能夠得到一致的表達(dá),或者更加抽象一些,我們所需要的只是知識(shí)的自洽性,而唯一性無(wú)疑是維持自洽性的一種廉價(jià)方法。特別是在一個(gè)不斷演化的系統(tǒng)中,保持形式上的唯一性可能是實(shí)現(xiàn)自洽性的唯一可行的方法。 ???? 但是, 我們需要認(rèn)識(shí)到知識(shí)的一致性與代碼的唯一性并不是等同的,例如同樣是釋放資源的函數(shù), 在不同的應(yīng)用情形下我們可能將其命名為close, 也可能是destroy, 或者是dispose, 如果我們使用一個(gè)接口IDisposable.dispose(), 則引入了一種形式上的唯一性要求. 在使用reflection的情況下, 我們可以放松要求, 不要求對(duì)象實(shí)現(xiàn)特定的接口, 只要提供指定的函數(shù)名(例如dispose)即可. 我們也可以更加寬容, 通過(guò)外部描述性數(shù)據(jù)指定函數(shù)的用途, 只要求概念上的一致性, 例如spring中通過(guò)destroy-method屬性指定對(duì)象資源釋放函數(shù). 沒(méi)有語(yǔ)言級(jí)別的形式唯一性, 我們就無(wú)法依賴于編譯器來(lái)維護(hù)其隱含的知識(shí)的一致性, 此時(shí)我們所能使用的通用方法就只有測(cè)試(test)了. 實(shí)際上, 很多知識(shí)上的自洽性要求都無(wú)法在程序中直接得到表達(dá), 而只能通過(guò)一個(gè)構(gòu)造的測(cè)試網(wǎng)絡(luò)來(lái)進(jìn)行驗(yàn)證.
???? 正如排他鎖(exclusive lock)是實(shí)現(xiàn)transaction的一種強(qiáng)形式一樣, 唯一性也是自洽性的一種強(qiáng)形式。在保持了唯一性的情況下,當(dāng)然不可能出現(xiàn)沖突的情況,也就自然的維持了系統(tǒng)的自洽性。但是,很多時(shí)候概念的多樣性也是我們不得不考慮的內(nèi)容。在C語(yǔ)言中, memmove函數(shù)的功能包括memcpy的功能,到底要不要取消memcpy以避免無(wú)謂的錯(cuò)誤可不是一件容易決定的事情. 在數(shù)學(xué)上,同一個(gè)定理可能存在著多種非平凡(non-trivial)的等價(jià)表述形式, 從表面上看,它們可以是完全不相關(guān)的,但是原理上是等價(jià)的. 而不同的表述往往適用于不同的應(yīng)用情形. 同樣的,在軟件系統(tǒng)中,It is ok to have more than one representation of a piece of knowledge provided an effective mechanism for ensuring consistency between them is engaged. 在軟件設(shè)計(jì)中, 引入中間層是在控制內(nèi)在統(tǒng)一性的同時(shí)獲得豐富的外在表現(xiàn)的一種重要方式. 在CORBA中idl編譯器將idl文件翻譯成不同程序語(yǔ)言的版本, 我們?cè)诔绦蛑惺褂玫氖翘囟ǔ绦蛘Z(yǔ)言的版本而不是直接的idl接口文件, 這些版本之間的自洽性是通過(guò)idl編譯器來(lái)保證的. idl編譯器所做的只是一對(duì)一的翻譯工作, 它本身并沒(méi)有提供額外的知識(shí), 而它所生成的各個(gè)程序語(yǔ)言版本所表達(dá)的知識(shí)也是相同的. 可以想見(jiàn), 一種更加復(fù)雜的,甚至是具備一定推理能力的引擎(engine)可以基于元知識(shí)進(jìn)行更加復(fù)雜的變換工作, 并可以融合其他外部的知識(shí), 最終輸出一系列自洽的表現(xiàn)結(jié)構(gòu).例如, 我們可以根據(jù)一個(gè)描述文件生成所有CRUD(Create Read Update Delete)操作的程序代碼和界面代碼. 這些生成的文件中可能存在著重復(fù)的代碼,可能重復(fù)的表達(dá)了某個(gè)知識(shí), 例如界面布局等, 但是它們之間通過(guò)引擎隱蔽的存在著穩(wěn)固的聯(lián)系
??? 軟件設(shè)計(jì)中總是存在著general與special的競(jìng)爭(zhēng), 一方面我們希望提出更加general的概念和方法, 在更大的范圍上捕獲更多的關(guān)聯(lián),? 另外一方面我們又希望在局部使用特殊定制的接口和實(shí)現(xiàn), 提高局部信息利用的效率, 很多時(shí)候兩者之間是存在一定的沖突的. 從實(shí)際操作的過(guò)程來(lái)看, general這個(gè)方向很難控制, 當(dāng)我們?cè)噲D提供更多的時(shí)候, 最終真正實(shí)現(xiàn)的多半只是更多的限制而不是更多的靈活性. 對(duì)于不是非常熟悉的領(lǐng)域, 我們很難避免各種意想不到的信息泄露, 最終它們會(huì)使得general的設(shè)計(jì)名存實(shí)亡. special的方向相對(duì)容易控制一些, 只要保證所有用到的參量都是目前必須的就可以了. ??? 現(xiàn)代數(shù)學(xué)技術(shù)與古典方法的一個(gè)鮮明區(qū)別在于, 傳統(tǒng)方法總是假設(shè)信息是完備的, 因而它試圖首先建立一個(gè)更加通用的模型, 解決一個(gè)更為一般性(往往更加復(fù)雜)的問(wèn)題, 然后再以這個(gè)通用問(wèn)題為基礎(chǔ)來(lái)解決我們的特定問(wèn)題. 例如為了估計(jì)某個(gè)隨機(jī)波動(dòng)造成的損失, 傳統(tǒng)方法將從估計(jì)隨機(jī)分布的密度函數(shù)開(kāi)始, 但是密度估計(jì)是統(tǒng)計(jì)學(xué)中的一個(gè)"終極問(wèn)題"(一旦密度函數(shù)已知, 我們就可以求解各階矩,從而解決各種統(tǒng)計(jì)問(wèn)題), 它需要大量觀測(cè)數(shù)據(jù)(信息)才有可能滿足漸進(jìn)估計(jì)所需要的數(shù)學(xué)條件. 而現(xiàn)代方法更加強(qiáng)調(diào)問(wèn)題的特殊性, 強(qiáng)調(diào)信息的不完備性, 因而傾向于直接對(duì)于給定的問(wèn)題建模, 因而模型中包含更少的參數(shù), 這樣我們才有可能得到更加穩(wěn)定的解. ??? 在軟件設(shè)計(jì)中我們遇到的最大的問(wèn)題也是信息不完備的問(wèn)題, 我們同樣需要注意避免把解決一個(gè)更為一般的問(wèn)題作為解決當(dāng)前問(wèn)題的一個(gè)中間步驟.
??? 軟件設(shè)計(jì)雖然是需要智力付出的一種過(guò)程,但是它并不意味著必然產(chǎn)生出一些創(chuàng)造性的東西. 一般的設(shè)計(jì)工作只是將業(yè)務(wù)架構(gòu)映射到一個(gè)通用的軟件技術(shù)架構(gòu)上. 這就如同大多數(shù)時(shí)候我們只是應(yīng)用某個(gè)算法來(lái)解決具體問(wèn)題, 而不是發(fā)明一個(gè)新的算法一樣. 最近所見(jiàn)的一些失敗的設(shè)計(jì), 其關(guān)鍵問(wèn)題往往不是簡(jiǎn)單的過(guò)度設(shè)計(jì)的問(wèn)題, 而完全是一種錯(cuò)誤的設(shè)計(jì). 當(dāng)我們?cè)噲D在軟件中創(chuàng)造一種新的關(guān)聯(lián)關(guān)系, 建立一種新的交互方式和交互規(guī)則的時(shí)候, 往往會(huì)走到錯(cuò)誤的方向上.
|