簡(jiǎn)述為什么要重視.NET代碼設(shè)計(jì)
.NET代碼設(shè)計(jì)的重點(diǎn)在于重視程度,也就是開(kāi)發(fā)人員需要集中思想,提前做好準(zhǔn)備工作。如果不重視,很可能出現(xiàn)太多的public會(huì)讓使用這些class的二次開(kāi)發(fā)的人員無(wú)所適從的局面。
為什么要重視.NET代碼設(shè)計(jì)?
在絕大多數(shù)項(xiàng)目中,尤其是在大型、資源缺少(這是軟件項(xiàng)目的典型現(xiàn)象)的項(xiàng)目中,正規(guī)的架構(gòu)可能只是解決系統(tǒng)級(jí)的事項(xiàng),而特意把大部分的設(shè)計(jì)工作留待代碼構(gòu)建階段去做。這可能會(huì)引發(fā)兩個(gè)問(wèn)題:
<!--[if !supportLists]-->1) <!--[endif]-->代碼構(gòu)建階段的設(shè)計(jì)分散保存在開(kāi)發(fā)人員的頭腦中,得不到有效的驗(yàn)證
<!--[if !supportLists]-->2) <!--[endif]-->由于開(kāi)發(fā)人員各有所長(zhǎng),也各有所短,往往在進(jìn)行了部分或完成代碼構(gòu)建后,問(wèn)題才能被明確。最終,不合理的設(shè)計(jì)導(dǎo)致大量的重構(gòu)工作,延誤項(xiàng)目工期。
設(shè)計(jì)工作千頭萬(wàn)緒,然而軟件設(shè)計(jì)的首要使命是“管理復(fù)雜度”;重點(diǎn)和難點(diǎn)是“管理變化”。雖然“管理變化”是“管理復(fù)雜度”的內(nèi)容之一,但由于“管理變化”具有突出的重要性,因此單獨(dú)進(jìn)行介紹。在代碼構(gòu)建整個(gè)過(guò)程中,所有設(shè)計(jì)工作必須充分考慮這兩個(gè)方面,從而實(shí)現(xiàn)高質(zhì)量代碼。
<!--[if !supportLists]-->1. <!--[endif]-->管理復(fù)雜度
復(fù)雜度來(lái)源:
<!--[if !supportLists]-->1) <!--[endif]-->用復(fù)雜的方法解決簡(jiǎn)單的問(wèn)題
這個(gè)現(xiàn)象通常出現(xiàn)在過(guò)度設(shè)計(jì)上:首先是簡(jiǎn)單的問(wèn)題復(fù)雜化,然后對(duì)復(fù)雜化的問(wèn)題使用復(fù)雜的方法。
如在10個(gè)有序數(shù)字中檢索某個(gè)數(shù)字。按照目前的CPU運(yùn)算速度,直接采用順序檢索簡(jiǎn)單有效。如果盲目考慮CPU運(yùn)算的因素,采用二分法檢索,反而導(dǎo)致邏輯冗余。但是,如果對(duì)位數(shù)過(guò)萬(wàn)的數(shù)組進(jìn)行檢索,則應(yīng)采用二分法或其它更有效的算法。
<!--[if !supportLists]-->2) <!--[endif]-->用簡(jiǎn)單但錯(cuò)誤的方法解決復(fù)雜的問(wèn)題
由于設(shè)計(jì)是一個(gè)由淺入深,不斷嘗試,不斷失敗,最終得到正確結(jié)果的過(guò)程,因此,在代碼構(gòu)建之初,當(dāng)設(shè)計(jì)者未能正確考慮復(fù)雜問(wèn)題的各個(gè)方面,將復(fù)雜問(wèn)題簡(jiǎn)單化后,將會(huì)導(dǎo)致“用簡(jiǎn)單但錯(cuò)誤的方法解決復(fù)雜的問(wèn)題”。
對(duì)此,我說(shuō)一下自己的一個(gè)切身體會(huì),在SharePoint開(kāi)發(fā)中,我們一個(gè)常見(jiàn)的問(wèn)題是“內(nèi)容數(shù)據(jù)庫(kù)非關(guān)系型數(shù)據(jù)庫(kù)”,雖然Moss提供了Lookup類型的字段來(lái)建立這種關(guān)系,但是存在兩個(gè)問(wèn)題,首先是不能跨站點(diǎn),更不能查詢多個(gè)列表的內(nèi)容,也不能綜合顯示多個(gè)字段的內(nèi)容。這些缺陷對(duì)于很多應(yīng)用來(lái)說(shuō),似乎是致命的。為此,我曾經(jīng)設(shè)計(jì)出同時(shí)支持跨網(wǎng)站、跨列表、多字段的自定義Field。但是設(shè)計(jì)中欠缺考慮,以致完成該Field的開(kāi)發(fā)后,一個(gè)顯著的缺陷讓我頭疼不已:將關(guān)聯(lián)關(guān)系存儲(chǔ)與字段值中,雖然在顯示、編輯和存儲(chǔ)上簡(jiǎn)化了編碼工作,但是卻未能解決關(guān)聯(lián)關(guān)系本質(zhì)上的問(wèn)題,從而導(dǎo)致了一系列更為復(fù)雜的問(wèn)題的出現(xiàn),如反向關(guān)聯(lián)查詢,關(guān)聯(lián)同步,以致于花費(fèi)了大量的精力來(lái)維護(hù)這種關(guān)系。
在該字段新版本的設(shè)計(jì)中,我將關(guān)聯(lián)關(guān)系單獨(dú)存儲(chǔ)在一個(gè)列表中,同時(shí)擴(kuò)展基內(nèi)容類型的New & Edit Form。嵌入維護(hù)不同列表間項(xiàng)與項(xiàng)關(guān)系的可視化展示與編輯部件(基于Silverlight實(shí)現(xiàn))。順便提一下,使用了多年的Flex,發(fā)現(xiàn)Silverlight除了在基礎(chǔ)組件庫(kù)上略遜色外,性能方面還是很不錯(cuò)的,尤其是繼承自NET的垃圾回收機(jī)制。
但是無(wú)論是Flex和Silverlight,都要注意它在圖形繪制方面的局限性,當(dāng)需要繪制的組件和圖表過(guò)多時(shí),盡量只繪制ViewPort中的部分,否則性能問(wèn)題將導(dǎo)致整個(gè)程序的崩潰。
總結(jié)一下,如果使用簡(jiǎn)單但錯(cuò)誤的方法解決復(fù)雜問(wèn)題,那么代價(jià)會(huì)在后面成幾何級(jí)數(shù)回饋給你。
套用一句話:代碼無(wú)小事!
<!--[if !supportLists]-->3) <!--[endif]-->用不恰當(dāng)?shù)膹?fù)雜方法解決復(fù)雜問(wèn)題
在舊有經(jīng)驗(yàn)的指導(dǎo)下,某些復(fù)雜問(wèn)題的表象會(huì)誤導(dǎo)設(shè)計(jì)者,將之當(dāng)作另外一個(gè)曾經(jīng)發(fā)生過(guò)的復(fù)雜問(wèn)題,而問(wèn)題本身的復(fù)雜程度也令人望而卻步,不愿做進(jìn)一步的分析,直接套用已有的解決方法雖然省事,卻有可能“用不恰當(dāng)?shù)膹?fù)雜方法解決復(fù)雜問(wèn)題”。
管理復(fù)雜度的方法:
<!--[if !supportLists]-->1) <!--[endif]-->任何人在同一時(shí)間只處理必不可少的復(fù)雜度
<!--[if !supportLists]-->a) <!--[endif]-->分類匯總
從問(wèn)題的領(lǐng)域著手,而不是從底層實(shí)現(xiàn)細(xì)節(jié)入手去編寫(xiě)程序,在最抽象的層次上工作,也能減少人的腦力負(fù)擔(dān)。
問(wèn)題的領(lǐng)域在不同抽象程度上,可以劃分為不同的級(jí)別:業(yè)務(wù),子系統(tǒng),功能模塊,類、子程序。通常,從較高的領(lǐng)域分解下來(lái),會(huì)有效的降低問(wèn)題的復(fù)雜程度。如在子系統(tǒng)級(jí)別確定好每個(gè)功能模塊接口,那么在進(jìn)入功能模塊領(lǐng)域后,你只需要關(guān)心少量的接口,而把更多的精力放在該功能的數(shù)據(jù)、邏輯和UI設(shè)計(jì)上。
C#等面向?qū)ο笳Z(yǔ)言可以輕易實(shí)現(xiàn)這種抽象,通過(guò)定義基類或抽象類(如C#中的abstract 關(guān)鍵字)。
<!--[if !supportLists]-->b) <!--[endif]-->一致的抽象
定義基類或抽象類時(shí),必須保持同級(jí)別抽象的一致性,摒除不必要的細(xì)節(jié)。
如ASP.NET編程中的Page和Control就屬于同一級(jí)別的抽象。 在Page類的實(shí)現(xiàn)中,它無(wú)需知道具體是什么類型的Control,是DataGrid還是TextBox,它根本不關(guān)心,它只關(guān)心Control類中的Render()可以提供什么樣的Html。而這個(gè)Control是紅的還是綠的,是方的還是圓的,根本不需要關(guān)心。如果編寫(xiě)Page類時(shí)調(diào)用了Control的一個(gè)子類,那抽象就不一致了,Page將自動(dòng)降級(jí),Page對(duì)于Control基類毫無(wú)意義。當(dāng)然,這樣的錯(cuò)誤誰(shuí)也不會(huì)去犯,僅僅用來(lái)說(shuō)明這個(gè)道理而已。
在SharePoint中,通常我們也會(huì)實(shí)現(xiàn)一些基礎(chǔ)類以提高團(tuán)隊(duì)的開(kāi)發(fā)效率,如BaseAjaxWebPart、BaseAjaxField等等。這些Base類中一定不要使用某些實(shí)現(xiàn)了特定細(xì)節(jié)的類或組件。
<!--[if !supportLists]-->2) <!--[endif]-->不要讓偶然性的復(fù)雜度無(wú)謂地快速增長(zhǎng)
偶然性的復(fù)雜度,即潛在的變化,包括:已知的未知、未知的未知。
對(duì)于未知的未知這種情況,我們可以不過(guò)多考慮,只需要根據(jù)經(jīng)驗(yàn)在任務(wù)工期上預(yù)留一定資源:時(shí)間、人力等。
對(duì)于已知的不確定的變化,在下面《管理變化》中簡(jiǎn)要介紹。
<!--[if !supportLists]-->2. <!--[endif]-->管理變化
沒(méi)有變化的程序是不可能存在的。但不能讓變化像病毒一樣蔓延到整個(gè)程序。
管理變化的手段比較多,如24種設(shè)計(jì)模式,大部分都是解決這個(gè)問(wèn)題的。但萬(wàn)變不離其宗,主要還是以下兩個(gè)方面:
<!--[if !supportLists]-->1) <!--[endif]-->封裝
通過(guò)有效的繼承和一致的抽象,可以確保發(fā)生變化時(shí),最小的代碼改動(dòng)量。
<!--[if !supportLists]-->2) <!--[endif]-->模塊化
通過(guò)對(duì)系統(tǒng)模塊化及劃分界面,確定接口,可以有效地將變化控制在模塊內(nèi)部。
使用封裝和模塊化來(lái)管理變化時(shí),必須注意隱藏信息:該使用private之處,絕不要使用protected。該使用protected之處,絕不要使用public。Internal也不要多用,它的作用域是程序集內(nèi)部,在編寫(xiě)代碼時(shí),往往跟public一樣混淆視聽(tīng),降低了代碼的可讀性。
太多的public會(huì)讓使用這些class的二次開(kāi)發(fā)的人員無(wú)所適從。充斥著非必要public成員的class,不符合管理復(fù)雜度的原則。有可能是設(shè)計(jì)原因,也有可能是未按照設(shè)計(jì)嚴(yán)格編碼。
.NET代碼設(shè)計(jì)的要點(diǎn)就介紹到這里。
原文標(biāo)題:談?wù)劥a設(shè)計(jì)
鏈接:http://www.cnblogs.com/ghner/archive/2009/09/05/1560998.html
【編輯推薦】