asp.net 多隆云:我和韋春花的關係是很純潔的;網易稱其和迷蝶女友的關係是培訓雙向選擇制;楊振寧言《易經》思想阻礙了中國近代科學的發展;邏輯學家論:概念間的關係有五種,即同一、包含、交叉、分離、互斥。而康托爾卻認為:關係是有序偶。
■
我要懶+笨 - 程式設計的終極目標
前不久社區裡出現題為《這樣的程式員是否算是一個合格的程式員?》的熱貼,樓主言一手下編碼花時間比別人多但代碼規範健壯,設問這樣的程式員是否可以留用且如何用?頓時掀起一片嘩然,展開在中國這個特定的環境下何為程式設計目標及相關項目管理之討論。心靈捕手運用老毛《矛盾論》之手法分析:"這個是市場和程式員的矛盾",稱"如果是小公司的話就別談什麼發展……時間為第一生產力……如果不是……就先解決管理和預算問題",我不禁開始歎服其處理方法之老辣。
撇開市場因素不談,程式設計的首要目標是去成功解決一現實問題。特別要注意的是,這個現實問題往往是動態、隨著時間的變化而變化的,所以我們的代碼要足夠健壯,以適應這個變化過程,而不應把目標定位在解決某一個靜態點上的現實問題;其次代碼及其結構應該是簡潔的,只有簡潔才能使編寫容易、修改容易、維護容易,最大程度地節約成本;再則代碼應該能夠重複使用,貪婪的想法是代碼不僅能夠在一個現實問題中重用以減少重複勞動,還能在不同現實問題的交叉部份重複使用;最後因為人對世界的認識是一個迭代過程和現實問題在不斷變化這兩個因素,所以程式應該是不影響原來代碼的前提下容易擴充。 理論上說理解"萬物皆對象,事事要封裝",綜合面對過程的經驗,你已經可以開始動手解決任何一個現實問題,但你會發現在整個開發過程中,僅憑目前的知識和見解,自己會被大量繁雜問題所纏繞,直至放棄為止,但這些原本完全可以避免!不管是這理論還是那設計模式,所有前人經驗總結的終極目標無非就是讓程式設計變成更簡潔、更容易、更快捷,明白這點,許多傑出的程式大師為何號稱自己是"懶+笨"之人也就不難理解。
■ 分離關係- 類的封裝與依賴
這個世界的事物不可能象一個個魯賓遜孤島散落在一望無際的海洋中,老死不相往來。相反,即使是中國最邊遠的墨脫縣,也依然與祖國保持著千絲萬縷的關係。《隨想五》中我們知道如何把現實空間的事物整理成對象並抽象成類,接下來梳理一下它們之間的關係。
首先我們以程式設計目標的視角來關注封裝的意義。站在航海者(類的使用者)的角度來看,他不必要瞭解島嶼的地理、社會等內部結構(迪米特法則Law of Demeter),而只需知道島嶼生產和需要什麼(類的功能)、找誰買賣(public成員介面)就行了,知道這些就能很快搭建起一個商業網路(設計應用程式)。而作為島嶼的統治者(類的實現者),他不必關注外界的風風雨雨,只需專註於島嶼內的生產和消費管理,統治者把這種現象稱之為高內聚。
封閉大大簡化了程式設計的複雜度,類間交流是通過一個狹窄的、經過良好限定的介面進行以保證類的可靠性。一方面類的使用者收集類快速開發程式,並且不試圖改變類的內部結構,另一方面類的實現者在不修改public成員介面的前提下可以自由地修改內部工作方式。
但類的單獨存在沒有任何意義!最普通的關係是某個類的執行個體使用另一個類的執行個體,如果商船泰坦尼克號需要夏威夷島提供貨物,那麼我們稱商船依賴(Dependency)於島嶼提供貨物,如圖1所示,而隨之而來的問題是,如果夏威夷島內部動亂(類的實現者修改public成員介面),將直接影響泰坦尼克號的正常工作,我們也將不得不重新組織商船類的內部結構以適應變化,前功盡棄。耦合是類間依賴程度的量度,對於變化可能性大的類在處理依賴關係時要盡量避免高耦合。
■ 概念的包含與交叉 - 類的合成與繼承
笨人是無法理解一個塞滿各種複雜功能的對象,所以類的第一設計原則是單一,對應問題空間中的一個概念。如果所對應的概念包含其它概念,卻為整體/ 部分關係,稱之為"has-a",我們就將這幾個類的執行個體合成(composition)一個新類。例如商船都有動力裝置,那麼我們就執行個體化一個動力裝置類,加上除動力裝置範疇以外的其它屬性和方法,即合成商船類,如圖2。合成具有極大的靈活性,且不破壞類的封裝。所以我們要盡量使用合成,少用下面介紹的繼承(合成複用原則Composite Reuse Principle)。
小時候的娛樂方式比較少,映入眼框的除了山水外,最多的莫過於五六十年代戰爭題材的老電影,裡頭人物臉譜化得嚴重,以至於我總認為,這個世界只有兩種人--好人和壞人,如果這個觀點成立,那麼合成將一往無前。但這個世界上,並不全是整體都由部份簡單疊加。現在讓我們假設這個世界的船隻有兩種用途:商船和戰船,從概念的角度來看,它們有相擬性,即概念交叉,如圖6-3左,同時它們也具有各自的特點,如果我們只是簡單地將商船和戰船分別抽象成兩個類,那麼將出現大量的類成員重複,所以需要構造一個機制來反映兩個概念的交叉關係,這就是繼承(inheritance)的由來,稱之為"is-a"。
繼承的特點是具有層次性,從圖的外形來看很象家譜樹,但用家譜樹來比喻繼承是愚蠢的,並沒有真正揭示繼承的實質,繼承的過程,就是從一般到特殊的過程,如圖3右所示。傳說中人類都是由非州一古猿的後代,事實與否我們先不討論,但與之類似的是,如果層層抽象,.NET的所有類都直接或間接派生於同一個基類--Object類。
類的繼承最直觀的用處在於複用,.NET技術給我們第一映像就是MS公司工程師們經過長期實踐提煉出的五千多個公用類,對於應用而言,它幾乎涵蓋了目前為止所有領域的一般化概念。在此基礎上通過適當的繼承與合成,我們很快就能構架出屬於自己的類系。
我們再以成員集合的觀點看待類,對於合成而言,其成員集合如下:
商船{位置,船向,動力裝置{動力值},移動(方向)}
引用動力值需要如此表達:商船.動力裝置.動力值(你可以嘗試一下把"."讀成"的"),而對繼承而言,其成員集合如下:
·商船{最大運載量,裝載(貨物),移動(方向)}
·戰船{火力值,戰鬥(船),移動(方向)}
可以看出船類的public成員變成商船類的public成員,表達為:商船.移動,而船類的private成員在商船類中被隱藏。這麼處理的依據是:因為船能移動,商船是船,所以商船也能移動(著名的亞裡士多德三段論)。有時我們希望衍生類別能訪問基類的某個成員但又對類以外的世界隱藏,如船的速度,各種種類的船都應該有速度,所以速度應該是基類成員,但我們不希望外界因素來直接修改速度值以破壞速度的電腦制(其同時受內外因素影響,由船的移動方法來計算),所以我們引入第三個存取控制符protected來修飾該類成員。
戰船有一個有趣的方法:戰鬥(船),介面的參數是船類的一個執行個體,也就是說戰船可以和任何一種船戰鬥,甭管是你商船還是戰船,所以我們可以這麼使用該方法: 俾斯麥.戰鬥(泰坦尼克號),即子類型可以替換基底類型(依然可以用亞裡士多德三段論來證明這個邏輯),這種替換方法稱之為裡氏代換原則(Liskov Substitution Principle),作用是減小方法實現的複雜度。
繼承機制有一個重要的缺陷,基類和衍生類別是強耦合關係,且破壞了封裝,由此帶來問題是:如果基類因為設計不當而進行修改,將影響所有衍生類別;另外,對於衍生類別的某個成員而言,你可能要花半天時間才能找到它究竟是在哪層實現,所以在設計過程中,一要盡量壓縮繼承的層數,二是堅持合成複用原則,能用合成就不用繼承。
Login類的類別檢視