走在架構師的大道上 Jack.Wang's home

          Java, C++, linux c, C#.net 技術,軟件架構,領域建模,IT 項目管理 Dict.CN 在線詞典, 英語學習, 在線翻譯

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            195 Posts :: 3 Stories :: 728 Comments :: 0 Trackbacks
          如果你想要制作易于設計、構建、測試及擴展的系統,正交性是一個十分關鍵的概念,但是,正交性的概念很少被直接講授,而常常是你學習的各種其他方法和技術的隱含特性。這是一個錯誤。一旦你學會了直接應用正交性原則,你將發現,你制作的系統的質量立刻就得到了提高。

          什么是正交性

          文本框:    “正交性”是從幾何學中借來的術語。如果兩條直線相交成直角,它們就是正交的,比如圖中的坐標軸。用向量術語說,這兩條直線互不依賴。沿著某一條直線移動,你投影到另一條直線上的位置不變。

            在計算技術中,該術語用于表示某種不相依賴性或是解耦性。如果兩個或更多事物中的一個發生變化,不會影響其他事物,這些事物就是正交的。在設計良好的系統中,數據庫代碼與用戶界面是正交的:你可以改動界面,而不影響數據庫;更換數據庫,而不用改動界面。

            在我們考察正交系統的好處之前,讓我們先看一看非正交系統。

          非正交系統

            你正乘坐直升機游覽科羅拉多大峽谷,駕駛員——他顯然犯了一個錯誤,在吃魚,他的午餐——突然呻吟起來,暈了過去。幸運的是,他把你留在了離地面100英尺的地方。你推斷,升降桿控制總升力,所以輕輕將其壓低可以讓直升機平緩降向地面。然而,當你這樣做時,卻發現生活并非那么簡單。直升機的鼻子向下,開始向左盤旋下降。突然間你發現,你駕駛的這個系統,所有的控制輸入都有次級效應。壓低左手的操作桿,你需要補償性地向后移動右手柄,并踩右踏板。但這些改變中的每一項都會再次影響所有其他的控制。突然間,你在用一個讓人難以置信的復雜系統玩雜耍,其中每一項改變都會影響所有其他的輸入。你的工作負擔異常巨大:你的手腳在不停地移動,試圖平衡所有交互影響的力量。

            直升機的各個控制器斷然不是正交的。

          正交的好處

            如直升機的例子所闡明的,非正交系統的改變與控制更復雜是其固有的性質。當任何系統的各組件互相高度依賴時,就不再有局部修正(local fix)這樣的事情。

          提示13

          Eliminate Effects Between Unrelated Things
          消除無關事物之間的影響

            我們想要設計自足(self-contained)的組件:獨立,具有單一、良好定義的目的(YourdonConstantine稱之為內聚cohesion[YC86])。如果組件是相互隔離的,你就知道你能夠改變其中之一,而不用擔心其余組件。只要你不改變組件的外部接口,你就可以放心:你不會造成波及整個系統的問題。

            如果你編寫正交的系統,你得到兩個主要好處:提高生產率與降低風險。

          提高生產率

          l       改動得以局部化,所以開發時間和測試時間得以降低。與編寫單個的大塊代碼相比,編寫多個相對較小的、自足的組件更為容易。你可以設計、編寫簡單的組件,對其進行單元測試,然后把它們忘掉——當你增加新代碼時,無須不斷改動已有的代碼。

          l       正交的途徑還能夠促進復用。如果組件具有明確而具體的、良好定義的責任,就可以用其最初的實現者未曾想象過的方式,把它們與新組件組合在一起。

          l       如果你對正交的組件進行組合,生產率會有相當微妙的提高。假定某個組件做M件事情,而另一個組件做N件事情。如果它們是正交的,而你把它們組合在一起,結果就能做M x N件事情。但是,如果這兩個組件是非正交的,它們就會重疊,結果能做的事情就更少。通過組合正交的組件,你的每一份努力都能得到更多的功能。

          降低風險

            正交的途徑能降低任何開發中固有的風險。

          l       有問題的代碼區域被隔離開來。如果某個模塊有毛病,它不大可能把病癥擴散到系統的其余部分。要把它切掉,換成健康的新模塊也更容易。

          l       所得系統更健壯。對特定區域做出小的改動與修正,你所導致的任何問題都將局限在該區域中。

          l       正交系統很可能能得到更好的測試,因為設計測試、并針對其組件運行測試更容易。

          l       你不會與特定的供應商、產品、或是平臺緊綁在一起,因為與這些第三方組件的接口將被隔離在全部開發的較小部分中。

          讓我們看一看在工作中應用正交原則的幾種方式。

          項目團隊

            你是否注意到,有些項目團隊很有效率,每個人都知道要做什么,并全力做出貢獻,而另一些團隊的成員卻老是在爭吵,而且好像無法避免互相妨礙?

            這常常是一個正交性問題。如果團隊的組織有許多重疊,各個成員就會對責任感到困惑。每一次改動都需要整個團隊開一次會,因為他們中的任何一個人都可能受到影響。

            怎樣把團隊劃分為責任得到了良好定義的小組,并使重疊降至最低呢?沒有簡單的答案。這部分地取決于項目本身,以及你對可能變動的區域的分析。這還取決于你可以得到的人員。我們的偏好是從使基礎設施與應用分離開始。每個主要的基礎設施組件(數據庫、通信接口、中間件層,等等)有自己的子團隊。如果應用功能的劃分顯而易見,那就照此劃分。然后我們考察我們現有的(或計劃有的)人員,并對分組進行相應的調整。

            你可以對項目團隊的正交性進行非正式的衡量。只要看一看,在討論每個所需改動時需要涉及多少人。人數越多,團隊的正交性就越差。顯然,正交的團隊效率也更高(盡管如此,我們也鼓勵子團隊不斷地相互交流)。

          設計

            大多數開發者都熟知需要設計正交的系統,盡管他們可能會使用像模塊化基于組件、或是分層這樣的術語描述該過程。系統應該由一組相互協作的模塊組成,每個模塊都實現不依賴于其他模塊的功能。有時,這些組件被組織為多個層次,每層提供一級抽象。這種分層的途徑是設計正交系統的強大方式。因為每層都只使用在其下面的層次提供的抽象,在改動底層實現、而又不影響其他代碼方面,你擁有極大的靈活性。分層也降低了模塊間依賴關系失控的風險。你將常常看到像下一頁的圖2.1這樣的圖表示的層次關系。

            對于正交設計,有一種簡單的測試方法。一旦設計好組件,問問你自己:如果我顯著地改變某個特定功能背后的需求,有多少模塊會受影響?在正交系統中,答案應


           

          2.1 典型的層次圖

          該是“一個”。移動GUI面板上的按鈕,不應該要求改動數據庫schema。增加語境敏感的幫助,也不應該改動記賬子系統。

            讓我們考慮一個用于監視和控制供暖設備的復雜系統。原來的需求要求提供圖形用戶界面,但后來需求被改為要增加語音應答系統,用按鍵電話控制設備。在正交地設計的系統中,你只需要改變那些與用戶界面有關聯的模塊,讓它們對此加以處理:控制設備的底層邏輯保持不變。事實上,如果你仔細設計你的系統結構,你應該能夠用同一個底層代碼庫支持這兩種界面。157頁的“它只是視圖”將討論怎樣使用模型-視圖-控制器(MVC)范型編寫解耦的代碼,該范型在這里的情況下也能很好地工作。

            還要問問你自己,你的設計在多大程度上解除了與現實世界中的的變化的耦合?你在把電話號碼當作顧客標識符嗎?如果電話公司重新分配了區號,會怎么樣?不要依賴你無法控制的事物屬性

          工具箱與庫

            在你引入第三方工具箱和庫時,要注意保持系統的正交性。要明智地選擇技術。

            我們曾經參加過一個項目,在其中需要一段Java代碼,既運行在本地的服務器機器上,又運行在遠地的客戶機器上。要把類按這樣的方式分布,可以選用RMICORBA。如果用RMI實現類的遠地訪問,對類中的遠地方法的每一次調用都可能會拋出異常;這意味著,一個幼稚的實現可能會要求我們,無論何時使用遠地類,都要對異常進行處理。在這里,使用RMI顯然不是正交的:調用遠地類的代碼應該不用知道這些類的位置。另一種方法——使用CORBA——就沒有施加這樣的限制:我們可以編寫不知道我們類的位置的代碼。

            在引入某個工具箱時(甚或是來自你們團隊其他成員的庫),問問你自己,它是否會迫使你對代碼進行不必要的改動。如果對象持久模型(object persistence scheme)是透明的,那么它就是正交的。如果它要求你以一種特殊的方式創建或訪問對象,那么它就不是正交的。讓這樣的細節與代碼隔離具有額外的好處:它使得你在以后更容易更換供應商。

            Enterprise Java BeansEJB系統是正交性的一個有趣例子。在大多數面向事務的系統中,應用代碼必須描述每個事務的開始與結束。在EJB中,該信息是作為元數據,在任何代碼之外,以聲明的方式表示的。同一應用代碼不用修改,就可以運行在不同的EJB事務環境中。這很可能是將來許多環境的模型。

            正交性的另一個有趣的變體是面向方面編程(Aspect-Oriented ProgrammingAOP,這是Xerox Parc的一個研究項目([KLM+97][URL 49])。AOP讓你在一個地方表達本來會分散在源碼各處的某種行為。例如,日志消息通常是在源碼各處、通過顯式地調用某個日志函數生成的。通過AOP,你把日志功能正交地實現到要進行日志記錄的代碼中。使用AOPJava版本,你可以通過編寫aspect、在進入類Fred的任何方法時寫日志消息:

              aspect Trace {

                advise * Fred.*(..) {

                  static before {

                    Log.write("-> Entering " + thisJoinPoint.methodName);

                  }

                }

              }

            如果你把這個方面編織weave)進你的代碼,就會生成追蹤消息。否則,你就不會看到任何消息。不管怎樣,你原來的源碼都沒有變化。

          編碼

            每次你編寫代碼,都有降低應用正交性的風險。除非你不僅時刻監視你正在做的事情,也時刻監視應用的更大語境,否則,你就有可能無意中重復其他模塊的功能,或是兩次表示已有的知識。

            你可以將若干技術用于維持正交性:

          l       讓你的代碼保持解耦。編寫“羞怯”的代碼——也就是不會沒有必要地向其他模塊暴露任何事情、也不依賴其他模塊的實現的模塊。試一試我們將在183頁的“解耦與得墨忒耳法則討論的得墨忒耳法則(Law of Demeter[LH89]。如果你需要改變對象的狀態,讓這個對象替你去做。這樣,你的代碼就會保持與其他代碼的實現的隔離,并增加你保持正交的機會。

          l       避免使用全局數據。每當你的代碼引用全局數據時,它都把自己與共享該數據的其他組件綁在了一起。即使你只想對全局數據進行讀取,也可能會帶來麻煩(例如,如果你突然需要把代碼改為多線程的)。一般而言,如果你把所需的任何語境(context)顯式地傳入模塊,你的代碼就會更易于理解和維護。在面向對象應用中,語境常常作為參數傳給對象的構造器。換句話說,你可以創建含有語境的結構,并傳遞指向這些結構的引用。

            《設計模式[GHJV95]一書中的Singleton(單體)模式是確保特定類的對象只有一個實例的一種途徑。許多人把這些singleton對象用作某種全局變量(特別是在除此而外不支持全局概念的語言中,比如Java)。使用singleton要小心——它們可能造成不必要的關聯。

          l       避免編寫相似的函數。你常常會遇到看起來全都很像的一組函數——它們也許在開始和結束處共享公共的代碼,中間的算法卻各有不同。重復的代碼是結構問題的一種癥狀。要了解更好的實現,參見《設計模式》一書中的Strategy(策略)模式。

            養成不斷地批判對待自己的代碼的習慣。尋找任何重新進行組織、以改善其結構和正交性的機會。這個過程叫做重構refactoring),它非常重要,所以我們專門寫了一節加以討論(見“重構”,184頁)

          測試

            正交地設計和實現的系統也更易于測試,因為系統的各組件間的交互是形式化的和有限的,更多的系統測試可以在單個的模塊級進行。這是好消息,因為與集成測試(integration testing)相比,模塊級(或單元)測試要更容易規定和進行得多。事實上,我們建議讓每個模塊都擁有自己的、內建在代碼中的單元測試,并讓這些測試作為常規構建過程的一部分自動運行(參見“易于測試的代碼”,189頁)。

            構建單元測試本身是對正交性的一項有趣測試。要構建和鏈接某個單元測試,都需要什么?只是為了編譯或鏈接某個測試,你是否就必須把系統其余的很大一部分拽進來?如果是這樣,你已經發現了一個沒有很好地解除與系統其余部分耦合的模塊。

            修正bug也是評估整個系統的正交性的好時候。當你遇到問題時,評估修正的局部化程度。

              你是否只改動了一個模塊,或者改動分散在整個系統的各個地方?當你做出改動時,它修正了所有問題,還是又神秘地出現了其他問題?這是開始運用自動化的好機會。如果你使用了源碼控制系統(在閱讀了86頁的“源碼控制”之后,你會使用的),當你在測試之后、把代碼簽回(check the code back)時,標記所做的bug修正。隨后你可以運行月報,分析每個bug修正所影響的源文件數目的變化趨勢。

          文檔

            也許會讓人驚訝,正交性也適用于文檔。其坐標軸是內容和表現形式。對于真正正交的文檔,你應該能顯著地改變外觀,而不用改變內容。現代的字處理器提供了樣式表和宏,能夠對你有幫助(參見“全都是寫”,248頁)。

          認同正交性

            正交性與27頁介紹的DRY原則緊密相關。運用DRY原則,你是在尋求使系統中的重復降至最小;運用正交性原則,你可降低系統的各組件間的相互依賴。這樣說也許有點笨拙,但如果你緊密結合DRY原則、運用正交性原則,你將會發現你開發的系統會變得更為靈活、更易于理解、并且更易于調試、測試和維護。

            如果你參加了一個項目,大家都在不顧一切地做出改動,而每一處改動似乎都會造成別的東西出錯,回想一下直升機的噩夢。項目很可能沒有進行正交的設計和編碼。是重構的時候了。

            另外,如果你是直升機駕駛員,不要吃魚……
          原文地址:http://book.csdn.net/bookfiles/102/1001022939.shtml





          本博客為學習交流用,凡未注明引用的均為本人作品,轉載請注明出處,如有版權問題請及時通知。由于博客時間倉促,錯誤之處敬請諒解,有任何意見可給我留言,愿共同學習進步。
          posted on 2008-09-18 10:18 Jack.Wang 閱讀(1954) 評論(2)  編輯  收藏 所屬分類: 架構師篇

          Feedback

          # re: 正交軟件架構方法[未登錄] 2008-09-18 12:15 rocket
          有一個csdn的copy,沒有引用說明。。。
          無語了。搶點擊不是這么騙得啊  回復  更多評論
            

          # re: 正交軟件架構方法 2008-09-18 12:49 ci
          不錯。。。  回復  更多評論
            

          主站蜘蛛池模板: 新乡市| 辽宁省| 株洲县| 武清区| 堆龙德庆县| 永嘉县| 雷州市| 定州市| 夏邑县| 楚雄市| 巩留县| 扶余县| 勃利县| 张家界市| 桃源县| 襄樊市| 大田县| 中牟县| 巴林右旗| 女性| 连州市| 汕头市| 桂阳县| 香河县| 洛宁县| 霸州市| 微山县| 共和县| 尉氏县| 江油市| 六安市| 万宁市| 治多县| 磐安县| 海南省| 天全县| 安岳县| 正阳县| 惠来县| 原阳县| 双鸭山市|