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類的類視圖
■ 我要懶+笨 - 程序設計的終極目標
前不久社區里出現題為《這樣的程序員是否算是一個合格的程序員?》的熱貼,樓主言一手下編碼花時間比別人多但代碼規范健壯,設問這樣的程序員是否可以留用且如何用?頓時掀起一片嘩然,展開在中國這個特定的環境下何為程序設計目標及相關項目管理之討論。心靈捕手運用老毛《矛盾論》之手法分析:"這個是市場和程序員的矛盾",稱"如果是小公司的話就別談什么發展……時間為第一生產力……如果不是……就先解決管理和預算問題",我不禁開始嘆服其處理方法之老辣。
撇開市場因素不談,程序設計的首要目標是去成功解決一現實問題。特別要注意的是,這個現實問題往往是動態的、隨著時間的變化而變化的,所以我們的代碼要足夠健壯,以適應這個變化過程,而不應把目標定位在解決某一個靜態點上的現實問題;其次代碼及其結構應該是簡潔的,只有簡潔才能使編寫容易、修改容易、維護容易,最大程度地節約成本;再則代碼應該能夠重復使用,貪婪的想法是代碼不僅能夠在一個現實問題中重用以減少重復勞動,還能在不同現實問題的交叉部份重復使用;最后因為人對世界的認識是一個迭代過程和現實問題在不斷變化這兩個因素,所以程序應該是不影響原來代碼的前提下容易擴展。 理論上說理解"萬物皆對象,事事要封裝",綜合面對過程的經驗,你已經可以開始動手解決任何一個現實問題,但你會發現在整個開發過程中,僅憑目前的知識和見解,自己會被大量繁雜問題所纏繞,直至放棄為止,但這些原本完全可以避免!不管是這理論還是那設計模式,所有前人經驗總結的終極目標無非就是讓程序設計變成更簡潔、更容易、更快捷,明白這點,許多杰出的程序大師為何號稱自己是"懶+笨"之人也就不難理解。
![]() |
■ 分離關系- 類的封裝與依賴
這個世界的事物不可能象一個個魯賓遜孤島散落在一望無際的海洋中,老死不相往來。相反,即使是中國最邊遠的墨脫縣,也依然與祖國保持著千絲萬縷的關系。《隨想五》中我們知道如何把現實空間的事物整理成對象并抽象成類,接下來梳理一下它們之間的關系。
首先我們以程序設計目標的視角來關注封裝的意義。站在航海者(類的使用者)的角度來看,他不必要了解島嶼的地理、社會等內部結構(迪米特法則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類的類視圖
![]() |