相信任何一位程序員都曾經見過面條狀的代碼,這種代碼給人留下的只是噩夢。面向對象能夠支持較好的代碼組織方式,基本的處理思路是先將問題于分而治之,然后再把分開的代碼整合起來。分而治之和整合,成為組織代碼的關鍵思路。
抽象是面向對象中優化代碼組織的一個重要的思路。抽象的目標是消除細節,讓開發人員的注意力集中在重要的目標之上。抽象的思路在計算機世界中無處不在。TCP協議棧的不同層次都提供一種抽象,對上層隱藏了下層的細節;業務模型也提供了一種抽象,對領域分析人員隱藏了具體技術細節;設計模式同樣提供了抽象,它隱藏了具體的實現細節;甚至匯編語言也是一種抽象,它隱藏了機器指令;而我們這里的討論也是一種抽象,因為我們忽略了應用類型、設計語言這些細節。對于開發人員來說,最重要的是識別出本層次的抽象,應該表現什么,應該隱藏什么。按照抽象的原則進行組織的設計將會是清晰的設計。
抽象的另一個好處是能夠便于交流,在設計模式未被提出之前,我們不得不花上大量的時間來澄清設計的思路,但是設計模型的抽象使得我們用一個簡單的名詞來提到一個設計思路,這就大大提高了溝通的效率。而UML語言的設計目標也是如此。
下面我們提到的一些具體的技術,包括委托、包、分層,他們都在不同的角度體現出了抽象的思路。
1.1 委托
委托是面向對象中最為常用的一種技巧,它是那么的基本,我們在整篇文章中到處可見它的蹤影。在一些重要的例子中,我們會指出它的用法,一般的我們就不說了,大家注意些就能夠觀察到。有人說,用C語言編寫代碼,代碼量超過10000行就會失控,用C++,這個限制可以放寬到100000行。這種說法是否正確我不敢肯定,但有一點是很明確的,面向對象語言要比非面向對象語言更加的結構化,能夠支持更大規模的軟件。那么,為什么呢?這個問題的答案并不是在面向對象語言本身,而是在其它非軟件的學科中。我們知道,在一個小企業中,他沒有什么管理成本,管理人員也很少。但是一個大企業,他就有很多的管理人員,因為企業規模增大之后,需要付出管理成本。這些管理人員的主要職責,就是管理那些能夠直接產生利益的工作人員。我們還知道,現代社會存在的基礎是分工,分工明確,每個人做的事情少而精,這樣整個社會就會有效率。而面向對象正體現了這兩種思路。首先,一個面向對象的軟件,其中一定會有很多什么都不做的代碼,只是簡單的把對它的調用轉給另一個對象的方法,有的有做一些判斷,或是類型處理,有的干脆什么都不做。這些代碼看起來并不產生效益,甚至有些浪費。但是它們就好像是企業的管理者一樣,雖然消耗了一些資源(所幸的是,面向對象語言設計的高效性讓這種資源消耗是可接受的),但是它們能夠讓其它產生效益的代碼工作的更加的有效。在后面的例子中,我們會看到很多這樣的代碼。其次,面向對象非常注重類的職責,每個類處理的事情盡可能的少,每個方法的目標都十分的單一。但是一件事情一定是幾個專業的類共同完成的,而不是象傳統方法那樣,一個函數一包到底。
委托使用的多寡和面向對象語言本身有一定的關系。委托從某個角度來說也可以算是一種重用的技術,在不支持多重繼承的語言中,委托將會更為重要一些。而支持多重繼承的語言中,從不同的父類繼承能夠替代一部分的委托使用。如果繼承和委托同樣都能夠實現目標,那么繼承要比委托更加簡單和直觀一些,但是多重繼承引入了額外的復雜性。事實上,以Java語言為代表的單根繼承的語言,向來都認為委托是比繼承更好重用方式。
在軟件過程中,通過委托來將代碼組織起來是非常普遍的做法。但要注意兩點問題。委托給誰,和如何委托。前者說的是調用哪一個方法,后者說的是方法的參數、返回值和異常。兩個都是基本問題,但卻是最重要也最容易忽略的問題。在異常的案例中,我們知道需要約定異常的處理方式,以及為異常提供文檔。委托的使用也是這樣,給出類的說明,這樣客戶端就知道如何使用這個類,統一類接口的設計方法,形成一種約定,即便沒有足夠的文檔也可以保證客戶端正確的使用類。相應的,如何委托需要注意的也是類似的問題。這里我們只是簡單的提到處理思路,因為后文中還會詳細的討論。
1.2 使用接口來組織代碼
繼承除了重用之外,另一個用處是定義規范。角色和演員模式中,兩個對象職責分離,演員負責實現人在多個環境中恒定不變的特征和行為。而角色用于表現人在不同環境中的不同行為。但是很多時候,為了方便起見,角色也應當能夠扮演演員的一部分職責。因此,我們就需要用到委托機制,角色本身并不真正回答問題,而是把問題轉交給演員。但是,這樣一來,演員和角色之間就缺少了一個規范,那些是演員負責的,那些是角色負責的,沒有一個清晰的界定。對客戶端也是一件困惑的事情。所以我們最好是為它們定一個規范:
|
PersonProfile是一個接口,這里之所以采用接口,是因為我們的目標在于定義一個規范,而不是進行代碼重用。比起實現繼承,接口繼承具有更佳的靈活性。定義了這個接口之后,兩者的職責就已經非常的明確了。這里使用到委托機制的這些代碼都可以算是管理代碼,本身并沒有太大的價值,它們的管理價值是在客戶端體現的。
看到了嗎,在實際運用中,一定是多個技法同時使用的?赡苡腥藛,我的設計思路和這不同。這并不是問題的關鍵,問題的關鍵在于,我們如何使用這些技法來描述問題、解決問題。在軟件開發的過程中,象這種代碼級別的規范是必須勤于制定的,因為這種規范往往是客戶調用方和代碼提供方的一種約定、一種契約。針對這些規范進行工作的銜接、文檔的撰寫、測試的設計都能夠達成事半功倍的效果。久而久之,在軟件設計中就能夠形成很明顯的"塊狀"的設計和代碼,這對軟件的質量是很有幫助的。
1.3 包
當早期編寫程序的時候,缺少一種對代碼的組織技術,因此在大學時代寫項目,靠的就是目錄結構。在開始設計軟件之前,會先在磁盤上建立需要的目錄,例如有的目錄用于存放全局的函數,有的目錄用于存放數據庫的DDL文件,有的目錄用于存放文檔。短短幾年后,我們已經不需要使用這么老土的技術了。在現代的語言中,有很多新的機制能夠幫助我們更好的組織代碼。
我們這里討論的包主要是對源代碼的組織,同時,各種平臺中還存在對執行碼的組織形式,例如Java平臺中的Jar、.Net平臺中的程序集。在C++和C#語言中,代碼組織形式為命名空間(namespace),在Java語言中,組織形式為包(Package),在Eiffel語言中,組織形式為群集(cluster)。它們產生理由各不相同,命名空間主要解決的是名字沖突的問題,而包提供了一種對類和子包的打包技術,并能夠和文件系統優雅的結合,此外,包還提供了額外的可見性(包內可見),群集技術就相對比較松散,它不是Eiffel語言的組織部分,僅僅是對源代碼進行打包而已。
包在軟件過程中是非常重要的,它是建立統一愿景的重要工具。軟件開發的初期,類的設計還不明確,這時候首先確定的就應該是包圖。包圖不但表現了對業務領域的初步劃分,還表現了開發團隊以往的經驗。
此外,我們知道,類和類之間最重要的就是它們相互作用,形成一個網絡。但是當這個網絡過于復雜的時候,我們就需要一些辦法來組織這張網,使它更加的清晰。包的作用就是對網絡進行分塊,并降低塊和塊之間的交互。記得設計模式中的fa?ade模式嗎,這個思路是一樣的。例如Java中包的成員能夠看見彼此之間的保護和私有成員,這種做法就是加強了包內的類之間的耦合度,限制了包間的耦合度。
在前一章中,我們很難為重用來定義規范,因為重用的設計需要對癥下藥,很難定義一個標準的設計。但是代碼組織則不同,可以想出很多代碼組織的規范。例如,命名規則、自文檔的代碼、業務實體的設計規范。代碼組織的規范應該從整體開始考慮,這樣的效果最為明顯。
所以,最佳的規范應該是從分層設計上和包的設計上入手。軟件按照什么樣的原則進行分層;每一個包的含義是什么,在分層設計中起到什么樣的作用;如何保存數據,如何處理數據,如何傳遞數據,如何表現數據。這些是非常值得,也是非常必須建立規范的。
設計上的規范能夠從整體上規范代碼,而代碼規范能夠在細節上規范代碼。代碼級別的規范有很多,例如sun的網站上就提供了很多的規范。
而像接口設計這樣的技巧就很難定義一個規范,所以它的規范和上一章制定重用規范的思路類似。
不過,一如之前的案例,制定規范只是第一步,更重要的是在后面。
之所以要組織代碼,其中的一個原因就是為了滿足組織的需要。在單人的開發環境下,代碼要不要組織都是次要的,只要開發人員本人沒有問題,那就沒有問題。但多人的開發環境下就不同了。不同的開發人員需要對軟件和軟件開發有著一致的了解。在組織環境中,每個開發人員都在一定的抽象層次上為其它開發人員提供服務。開發人員之間通過這種提供服務和使用服務的方式耦合起來,如果服務效果不好,那么開發就會打折扣。
一個人寫出的類或框架,別人用起來是否方便,理解起來是否容易,這些都很重要。在上面討論接口的例子中,我們為什么一邊要分離兩者的職責,一邊又要讓角色對象具有演員對象的特征呢?既然是這樣,用一個單獨的類解決它們不是更好嗎,何必要多此一舉呢?注意到,這是兩個層面的問題。一個是分工問題,一個是便利問題;叵胍郧拔覀兝U費的時候,需要去電業局繳電費,去自來水公司繳水費,很不方便,后來發明了銀行代扣,這樣,原先要去好幾個地方的,現在只需要去一個地方。角色和演員就好比電業局和自來水公司,它們的職責不同。但是這對客戶端來說是不方便的,為了獲取演員的姓名,我們必須這樣:
|
為了不需要在演員和角色之間切換來切換去,我們希望客戶端的代碼能夠簡單一些:
|
所以呢,這時候角色就變成了銀行(當然也可以引入第三個對象來扮演銀行,有的例子中確實需要這樣,但這里沒有必要)?蛻舳说拇a就簡化了,至于怎么獲得姓名,那是你角色的事情,和客戶端沒有什么關系。那么,PersonProfile接口是一個什么東西呢?其實就是一種約定,是我們知道在銀行可以代繳水電費這樣一種知識。如果缺乏這種知識,我們就會向銀行詢問,你這里能代繳些什么費用?銀行就會回答說,可以代繳水電費。這就使得接口變成了一種動態接口了。
如果我們把類本身看作是服務端,對類的調用看作是客戶端。那么這里表達了一種面向對象的設計思路,就是寧可服務端復雜,也要使客戶端簡單。這其實也就是抽象的思路。
面向對象設計的這種特點決定了面向對象軟件開發過程中的一些特色。首先,軟件設計的工作量分配應當基于類進行。每一個開發人員都是為其它開發人員服務,任何一個人都需要用到其他人的代碼。這種關系是分工的關系,要比非面向對象開發的分工化程度高許多。當然,這種分工是有著一定的原則的,是按照軟件的分層體系來劃分呢?還是按照業務需求來劃分。只要能夠劃分清楚,任何一種方式都是可接受的。
其次,每個人保證代碼質量,并維護自己的代碼。在活用XP一文中,我們曾經討論,是否采用代碼集體所有制。我比較傾向于個人代碼所有制,因為在這種分工的情況下,代碼的質量和接口的穩定是非常重要的。所以,采用面向對象設計的團隊,往往能夠同時進步。正是因為客戶端的要求非常的嚴格,迫使服務端不斷的改進。
再次,對使用程度不同的類進行不同的資源分派。雖然每個類都會被其它人所使用,但有些類是很重要的,例如基礎的類,或是重要的業務實體。這些類被頻繁使用,保證它們的質量。
所以,代碼組織的優劣對于組織來說是非常重要的,但是它還需要有過程的保證。
執行規范需要有相應的過程保證。而規范的執行結果也同樣需要有過程的保證?赡芎芏嗟能浖M織都擁有自己的軟件過程,但是過程是否涉及代碼組織的問題?可能有,也可能沒有。代碼組織的上游是設計的組織,而設計組織的上游是需求的組織。上游水源的清澈與否直接影響到下游。所以在開發過程中,有兩件事情是非常重要的,一是盡可能保證需求組織的正確性,而是保證代碼的組織能夠正確的反映需求。要做好這兩點并不容易,更多的情況是,你以為自己已經做好了,其實未必。更好的做法是要求團隊具備快速變化的能力,代碼的組織是一個演進的過程,或者說是一個重構的過程。不斷的改進,才有可能帶來優秀的組織方式,不去改進,原本優秀的組織方式也會變得丑陋。
關于作者 林星,辰訊軟件工作室項目管理組資深項目經理,有多年項目實施經驗。辰訊軟件工作室致力于先進軟件思想、軟件技術的應用,主要的研究方向在于軟件過程思想、Linux集群技術、OO技術和軟件工廠模式。您可以通過電子郵件 iamlinx@21cn.com 和他聯系。 |
文章來源于領測軟件測試網 http://www.kjueaiud.com/
版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
技術支持和業務聯系:info@testage.com.cn 電話:010-51297073
老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月