<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    GHawk

    #

    敏捷軟件開發 讀書筆記 (4)——OO五大原則(3.LSP——里氏替換原則)

    OCP作為OO的高層原則,主張使用“抽象(Abstraction)”和“多態(Polymorphism)”將設計中的靜態結構改為動態結構,維持設計的封閉性。

    “抽象”是語言提供的功能?!岸鄳B”由繼承語義實現。

    如此,問題產生了:“我們如何去度量繼承關系的質量?”

    Liskov于1987年提出了一個關于繼承的原則“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“繼承必須確保超類所擁有的性質在子類中仍然成立?!币簿褪钦f,當一個子類的實例應該能夠替換任何其超類的實例時,它們之間才具有is-A關系。

    該原則稱為Liskov Substitution Principle——里氏替換原則。林先生在上課時風趣地稱之為“老鼠的兒子會打洞”。^_^

    我們來研究一下LSP的實質。學習OO的時候,我們知道,一個對象是一組狀態和一系列行為的組合體。狀態是對象的內在特性,行為是對象的外在特性。LSP所表述的就是在同一個繼承體系中的對象應該有共同的行為特征。

    這一點上,表明了OO的繼承與日常生活中的繼承的本質區別。舉一個例子:生物學的分類體系中把企鵝歸屬為鳥類。我們模仿這個體系,設計出這樣的類和關系。

     lsp-fig1.jpg

    類“鳥”中有個方法fly,企鵝自然也繼承了這個方法,可是企鵝不能飛阿,于是,我們在企鵝的類中覆蓋了fly方法,告訴方法的調用者:企鵝是不會飛的。這完全符合常理。但是,這違反了LSP,企鵝是鳥的子類,可是企鵝卻不能飛!需要注意的是,此處的“鳥”已經不再是生物學中的鳥了,它是軟件中的一個類、一個抽象。

    有人會說,企鵝不能飛很正常啊,而且這樣編寫代碼也能正常編譯,只要在使用這個類的客戶代碼中加一句判斷就行了。但是,這就是問題所在!首先,客戶代碼和“企鵝”的代碼很有可能不是同時設計的,在當今軟件外包一層又一層的開發模式下,你甚至根本不知道兩個模塊的原產地是哪里,也就談不上去修改客戶代碼了。客戶程序很可能是遺留系統的一部分,很可能已經不再維護,如果因為設計出這么一個“企鵝”而導致必須修改客戶代碼,誰應該承擔這部分責任呢?(大概是上帝吧,誰叫他讓“企鵝”不能飛的。^_^)“修改客戶代碼”直接違反了OCP,這就是OCP的重要性。違反LSP將使既有的設計不能封閉!

    修正后的設計如下:

     lsp-fig2.jpg

    但是,這就是LSP的全部了么?書中給了一個經典的例子,這又是一個不符合常理的例子:正方形不是一個長方形。這個悖論的詳細內容能在網上找到,我就不多廢話了。

    LSP并沒有提供解決這個問題的方案,而只是提出了這么一個問題。

    于是,工程師們開始關注如何確保對象的行為。1988年,B. Meyer提出了Design by Contract(契約式設計)理論。DbC從形式化方法中借鑒了一套確保對象行為和自身狀態的方法,其基本概念很簡單:

    1. 每個方法調用之前,該方法應該校驗傳入參數的正確性,只有正確才能執行該方法,否則認為調用方違反契約,不予執行。這稱為前置條件(Pre-condition)。
    2. 一旦通過前置條件的校驗,方法必須執行,并且必須確保執行結果符合契約,這稱之為后置條件(Post-condition)。
    3. 對象本身有一套對自身狀態進行校驗的檢查條件,以確保該對象的本質不發生改變,這稱之為不變式(Invariant)。

    以上是單個對象的約束條件。為了滿足LSP,當存在繼承關系時,子類中方法的前置條件必須與超類中被覆蓋的方法的前置條件相同或者更寬松;而子類中方法的后置條件必須與超類中被覆蓋的方法的后置條件相同或者更為嚴格。

    一些OO語言中的特性能夠說明這一問題:

    • 繼承并且覆蓋超類方法的時候,子類中的方法的可見性必須等于或者大于超類中的方法的可見性,子類中的方法所拋出的受檢異常只能是超類中對應方法所拋出的受檢異常的子類。
      public class SuperClass{
          
      public void methodA() throws IOException{}
      }


      public class SubClassA extends SuperClass{
          
      //this overriding is illegal.
          private void methodA() throws Exception{}
      }


      public class SubClassB extends SuperClass{
          
      //this overriding is OK.
          public void methodA() throws FileNotFoundException{}
      }

    • 從Java5開始,子類中的方法的返回值也可以是對應的超類方法的返回值的子類。這叫做“協變”(Covariant)
      public class SuperClass {
          
      public Number caculate(){
              
      return null;
          }

      }


      public class SubClass extends SuperClass{
          
      //only compiles in Java 5 or later.
          public Integer caculate(){
              
      return null;
          }

      }

    可以看出,以上這些特性都非常好地遵循了LSP。但是DbC呢?很遺憾,主流的面向對象語言(不論是動態語言還是靜態語言)還沒有加入對DbC的支持。但是隨著AOP概念的產生,相信不久DbC也將成為OO語言的一個重要特性之一。

    一些題外話:

    前一陣子《敲響OO時代的喪鐘》和《喪鐘為誰而鳴》兩篇文章引來了無數議論。其中提到了不少OO語言的不足。事實上,遵從LSP和OCP,不管是靜態類型還是動態類型系統,只要是OO的設計,就應該對對象的行為有嚴格的約束。這個約束并不僅僅體現在方法簽名上,而是這個具體行為的本身。這才是LSP和DbC的真諦。從這一點來說并不能說明“萬事萬物皆對象”的動態語言和“C++,Java”這種“按接口編程”語言的優劣,兩類語言都有待于改進。莊兄對DJ的設想倒是開始引入DbC的概念了。這一點還是非常值得期待的。^_^
    另外,接口的語義正被OCP、LSP、DbC這樣的概念不斷地強化,接口表達了對象行為之間的“契約”關系。而不是簡單地作為一種實現多繼承的語法糖。

    posted @ 2006-01-18 18:12 GHawk 閱讀(3973) | 評論 (2)編輯 收藏

    敏捷軟件開發 讀書筆記 (3)——OO五大原則(2.OCP——開閉原則)

    開閉原則很簡單,一句話:“Closed for Modification; Open for Extension”——“對變更關閉;對擴展開放”。開閉原則其實沒什么好講的,我將其歸結為一個高層次的設計總則。就這一點來講,OCP的地位應該比SRP優先。

    OCP的動機很簡單:軟件是變化的。不論是優質的設計還是低劣的設計都無法回避這一問題。OCP說明了軟件設計應該盡可能地使架構穩定而又容易滿足不同的需求。

    為什么要OCP?答案也很簡單——重用。

    “重用”,并不是什么軟件工程的專業詞匯,它是工程界所共用的詞匯。早在軟件出現前,工程師們就在實踐“重用”了。比如機械產品,通過零部件的組裝得到最終的能夠使用的工具。由于機械部件的設計和制造過程是極其復雜的,所以互換性是一個重要的特性。一輛車可以用不同的發動機、不同的變速箱、不同的輪胎……很多東西我們直接買來裝上就可以了。這也是一個OCP的例子。(可能是由于我是搞機械出身的吧,所以就舉些機械方面的例子^_^)。

    如何在OO中引入OCP原則?把對實體的依賴改為對抽象的依賴就行了。下面的例子說明了這個過程:

    05賽季的時候,一輛F1賽車有一臺V10引擎。但是到了06賽季,國際汽聯修改了規則,一輛F1賽車只能安裝一臺V8引擎。車隊很快投入了新賽車的研發,不幸的是,從工程師那里得到消息,舊車身的設計不能夠裝進新研發的引擎。我們不得不為新的引擎重新打造車身,于是一輛新的賽車誕生了。但是,麻煩的事接踵而來,國際汽聯頻頻修改規則,搞得設計師在“賽車”上改了又改,最終變得不成樣子,只能把它廢棄。

    OCP-fig1.JPG

    為了能夠重用這輛昂貴的賽車,工程師們提出了解決方案:首先,在車身的設計上預留出安裝引擎的位置和管線。然后,根據這些設計好的規范設計引擎(或是引擎的適配器)。于是,新的賽車設計方案就這樣誕生了。

     OCP-fig2.JPG

    顯然,通過重構,這里應用的是一個典型的Bridge模式。這個實現的關鍵之處在于我們預先給引擎留出了位置!我們不必因為對引擎的規則的頻頻變更而制造相當多的車身,而是盡可能地沿用和改良現有的車身。
    說到這里,想說一說OO設計的一個誤區。
    學習OO語言的時候,為了能夠說明“繼承”(或者說“is-a”)這個概念,教科書上經常用實際生活中的例子來解釋。比如汽車是車,電車是車,F1賽車是汽車,所以車是汽車、電車、F1賽車的上層抽象。這個例子并沒有錯。問題是,這樣的例子過于“形象”了!如果OO設計直接就可以將現實生活中的概念引用過來,那也就不需要什么軟件工程師了!OO設計的關鍵概念是抽象。如果沒有抽象,那所有的軟件工程師的努力都是徒勞的。因為如果沒有抽象,我們只能去構造世界中每一個對象。上面這個例子中,我們應該看到“引擎”這個抽象的存在,因為車隊的工程師們為它預留了位置,為它制定了設計規范。
    上面這個設計也實現了后面要說的DIP(依賴倒置原則)。但是請記住,OCP是OO設計原則中高層次的原則,其余的原則對OCP提供了不同程度的支持。為了實現OCP,我們會自覺或者不自覺地用到其它原則或是諸如Bridge、Decorator等設計模式。然而,對于一個應用系統而言,實現OCP并不是設計目的,我們所希望的只是一個穩定的架構。所以對OCP的追求也應該適可而止,不要陷入過渡設計。正如Martin本人所說:“No significant program can be 100% closed.”“Closure not complete but strategic”

    (下一篇就要講LSP了,我覺得這是意義最為重要的OO設計原則,它直指當今主流OO語言的軟肋,點出了OO設計的精髓。)

    posted @ 2006-01-18 00:26 GHawk 閱讀(7575) | 評論 (7)編輯 收藏

    開源 Java 測試框架(轉)

    from  http://blog.csdn.net/wangyihust/archive/2006/01/02/568616.aspx

     JUnit   

    JUnit是由 Erich Gamma 和 Kent Beck 編寫的一個回歸測試框架(regression testing framework)。Junit測試是程序員測試,即所謂白盒測試,因為程序員知道被測試的軟件如何(How)完成功能和完成什么樣(What)的功能。Junit是一套框架,繼承TestCase類,就可以用Junit進行自動測試了。

    http://www.junit.org/

     Cactus   

    Cactus是一個基于JUnit框架的簡單測試框架,用來單元測試服務端Java代碼。Cactus框架的主要目標是能夠單元測試服務端的使用Servlet對象的Java方法如HttpServletRequest,HttpServletResponse,HttpSession等。

    http://jakarta.apache.org/cactus/

     Abbot   

    Abbot是一個用來測試Java GUIs的框架。用簡單的基于XML的腳本或者Java代碼,你就可以開始一個GUI。

    http://abbot.sourceforge.net/

     JUnitPerf   

    Junitperf實際是junit的一個decorator,通過編寫用于junitperf的單元測試,我們也可使測試過程自動化。

    http://www.clarkware.com/software/JUnitPerf.html

     DbUnit   

    DbUnit是為數據庫驅動的項目提供的一個對JUnit 的擴展,除了提供一些常用功能,它可以將你的數據庫置于一個測試輪回之間的狀態。

    http://dbunit.sourceforge.net/

     Mockrunner   

    Mockrunner用在J2EE環境中進行應用程序的單元測試。它不僅支持Struts actions, servlets,過濾器和標簽類還包括一個JDBC和一個JMS測試框架,可以用于測試基于EJB的應用程序。

    http://mockrunner.sourceforge.net/index.html

     DBMonster   

    DBMonster是一個用生成隨機數據來測試SQL數據庫的壓力測試工具。

    http://dbmonster.kernelpanic.pl/

     MockEJB   

    MockEJB是一個不需要EJB容器就能運行EJB并進行測試的輕量級框架。

    http://mockejb.sourceforge.net/

     StrutsTestCase   

    StrutsTestCase 是Junit TestCase類的擴展,提供基于Struts框架的代碼測試。StrutsTestCase同時提供Mock 對象方法和Cactus方法用來實際運行Struts ActionServlet,你可以通過運行servlet引擎來測試。因為StrutsTestCase使用ActionServlet控制器來測試你的代碼,因此你不僅可以測試Action對象的實現,而且可以測試mappings,from beans以及forwards聲明。StrutsTestCase不啟動servlet容器來測試struts應用程序(容器外測試)也屬于Mock對象測試,但是與EasyMock不同的是,EasyMock是提供了創建Mock對象的API,而StrutsTest則是專門負責測試Struts應用程序的Mock對象測試框架。

    http://strutstestcase.sourceforge.net/

     JFCUnit   

    JFCUnit使得你能夠為Java偏移應用程序編寫測試例子。它為從用代碼打開的窗口上獲得句柄提供了支持;為在一個部件層次定位部件提供支持;為在部件中發起事件(例如按一個按鈕)以及以線程安全方式處理部件測試提供支持。

    http://jfcunit.sourceforge.net/

     JTestCase   

    JTestCase 使用XML文件來組織多測試案例數據,聲明條件(操作和期望的結果),提供了一套易于使用的方法來檢索XML中的測試案例,按照數據文件的定義來聲明結果。

    http://jtestcase.sourceforge.net/

     SQLUnit   

    SQLUnit是一個單元測試框架,用于對數據庫存儲過程進行回歸測試。用 Java/JUnit/XML開發。

    http://sqlunit.sourceforge.net

     JTR   

    JTR (Java Test Runner)是一個開源的基于反轉控制(IOC)的J2EE測試框架。它允許你構建復雜的J2EE測試套件(Test Suites)并連到應用服務器執行測試,可以包括多個測試實例。JTR的licensed是GPL協議。

    http://jtrunner.sourceforge.net/

     Marathon   

    Marathon是一個針對使用Java/Swing開發GUI應用程序的測試框架,它由recorder, runner 和 editor組成,測試腳本是python代碼。Marathon的焦點是放在最終用戶的測試上。

    http://marathonman.sourceforge.net

     TestNG   

    TestNG是根據JUnit 和 NUnit思想而構建的一個測試框架,但是TestNG增加了許多新的功能使得它變得更加強大與容易使用比如:
    *支持JSR 175注釋(JDK 1.4利用JavaDoc注釋同樣也支持)
    *靈活的Test配置
    *支持默認的runtime和logging JDK功能
    *強大的執行模型(不再TestSuite)
    *支持獨立的測試方法。

    http://testng.org/

     Surrogate Test framework   

    Surrogate Test framework是一個值得稱贊單元測試框架,特別適合于大型,復雜Java系統的單元測試。這個框架能與JUnit,MockEJB和各種支持模擬對象(mock object )的測試工具無縫給合。這個框架基于AspectJ技術。

    http://surrogate.sourceforge.net

     MockCreator   

    MockCreator可以為給定的interface或class生成模擬對象(Mock object)的源碼。

    http://mockcreator.sourceforge.net/

     jMock   

    jMock利用mock objects思想來對Java code進行測試。jMock具有以下特點:容易擴展,讓你快速簡單地定義mock objects,因此不必打破程序間的關聯,讓你定義靈活的超越對象之間交互作用而帶來測試局限,減少你測試地脆弱性。

    http://www.jmock.org/

     EasyMock   

    EasyMock為Mock Objects提供接口并在JUnit測試中利用Java的proxy設計模式生成它們的實例。EasyMock最適合于測試驅動開發。

    http://www.easymock.org/

     The Grinder   

    The Grinder是一個負載測試框架。在BSD開源協議下免費使用。

    http://grinder.sourceforge.net/

     XMLUnit   

    XMLUnit不僅有Java版本的還有.Net版本的。Java開發的XMLUnit提供了兩個JUnit 擴展類XMLAssert和XMLTestCase,和一組支持的類。這些類可以用來比較兩張XML之間的不同之處,展示XML利用XSLT來,校驗XML,求得XPath表達式在XML中的值,遍歷XML中的某一節點利DOM展開。

    http://xmlunit.sourceforge.net/

     Jameleon   

    Jameleon一個自動化測試工具。它被用來測試各種各樣的應用程序,所以它被設計成插件模式。為了使整個測試過程變得簡單Jameleon提供了一個GUI,因此Jameleon實現了一個Swing 插件。

    http://jameleon.sourceforge.net/index.html

     J2MEUnit   

    J2MEUnit是應用在J2ME應用程序的一個單元測試框架。它基于JUnit。

    http://j2meunit.sourceforge.net/

     Jetif   

    Jetif是一個用純Java實現的回歸測試框架。它為Java程序單元測試以及功能測試提供了一個簡單而且可 伸縮的架構,可以用于個人開發或企業級開發的測試。它容易使用,功能強大,而且擁有一些企業級測試的重要功能。Jetif來源于JUnit, JTestCase以及TestNG的啟發,有幾個基本的概念直接來自于JUnit, 比如說斷言機制,Test Listener的概念,因此從JUnit轉到Jetif是非常容易的。

    http://jetif.sourceforge.net/

     GroboUtils   

    GroboUtils使得擴展Java測試變得可能。它包括用在Java不同方面測試的多個子項目。在GroboUtils中最常被到的工具是:多線程測試(multi-threaded tests),整體單元測試(hierarchial unit tests),代碼覆蓋工具(code coverage tool)。

    http://groboutils.sourceforge.net/

     Testare   

    TESTARE是用來簡化分布式應用程序(比如:在SERVLETS,JMS listeners, CORBA ORBs或RMI環境下)測試開發過程的一個測試框架。

    https://testare.dev.java.net/

    posted @ 2006-01-10 10:41 GHawk 閱讀(1247) | 評論 (0)編輯 收藏

    敏捷軟件開發 讀書筆記 (2)——OO五大原則(1.SRP 單一職責原則)

          一點說明:OO的五大原則是指SRP、OCP、LSP、DIP、ISP。這五個原則是書中所提到的。除此之外,書中還提到一些高層次的原則用于組織高層的設計元素,這些放到下次再寫。當然,OO設計的原則可能不止這五個,希望大家多提寶貴意見,多多交流。

          在學習和使用OO設計的時候,我們應該明白:OO的出現使得軟件工程師們能夠用更接近真實世界的方法描述軟件系統。然而,軟件畢竟是建立在抽象層次上的東西,再怎么接近真實,也不能替代真實或被真實替代。

          OO設計的五大原則之間并不是相互孤立的。彼此間存在著一定關聯,一個可以是另一個原則的加強或是基礎。違反其中的某一個,可能同時違反了其余的原則。因此應該把這些原則融會貫通,牢記在心!

    1. SRP(Single Responsibility Principle 單一職責原則)
          單一職責很容易理解,也很容易實現。所謂單一職責,就是一個設計元素只做一件事。什么是“只做一件事”?簡單說就是少管閑事。現實中就是如此,如果要你專心做一件事情,任何人都有信心可以做得很出色。但如果,你整天被亂七八糟的事所累,還有心思和精力把每件事都作好么?
    fig-1.JPG
         “單一職責”就是要在設計中為每種職責設計一個類,彼此保持正交,互不干涉。這個雕塑(二重奏)就是正交的一個例子,鋼琴家和小提琴家各自演奏自己的樂譜,而結果就是一個和諧的交響樂。當然,真實世界中,演奏小提琴和彈鋼琴的必須是兩個人,但是在軟件中,我們往往會把兩者甚至更多攪和到一起,很多時候只是為了方便或是最初設計的時候沒有想到。 

          這樣的例子在設計中很常見,書中就給了一個很好的例子:調制解調器。這是一個調制解調器最基本的功能。但是這個類事實上完成了兩個職責:連接的建立和中斷、數據的發送和接收。顯然,這違反了SRP。這樣做會有潛在的問題:當僅需要改變數據連接方式時,必須修改Modem類,而修改Modem類的結果就是使得任何依賴Modem類的元素都需要重新編譯,不管它是不是用到了數據連接功能。解決的辦法,書中也已經給出:重構Modem類,從中抽出兩個接口,一個專門負責連接、另一個專門負責數據發送。依賴Modem類的元素也要做相應的細化,根據職責的不同分別依賴不同的接口。最后由ModemImplementation類實現這兩個接口。
    fig-2.JPG

          從這個例子中,我們不難發現,違反SRP通常是由于過于“真實”地設計了一個類所造成的。因此,解決辦法是往更高一層進行抽象化提取,將對某個具體類的依賴改變為對一組接口或抽象類的依賴。當然,這個抽象化的提取應該根據需要設計,而不是盲目提取。比如剛才這個Modem的例子中,如果有必要,還可以把DataChannel抽象為DataSender和DataReceiver兩個接口。
     

    posted @ 2006-01-09 21:17 GHawk 閱讀(5557) | 評論 (5)編輯 收藏

    敏捷軟件開發 讀書筆記 (1)——設計的目標

    軟件設計是一種抽象活動,設計所要實現的是產出代碼。就這一點來說,任何人都會設計。但是,正如我們日常生活中所耳聞目睹或親身經歷,設計有優劣之分。

    從項目管理的角度去理解,設計是為了滿足涉眾(Stakeholders)的需求。顯然,一個設計應該滿足客戶對系統的功能及非功能需求。但單是滿足了這一點,并不能稱為一個好的設計。因為開發者同樣屬于涉眾!而開發者的需求又是怎樣的呢?至少,應該有以下幾條吧:

    • 老板希望軟件交付后,不應該有很高的維護成本。如果開發人員為了維護而經常出差或者加班且久久不能投入新項目,顯然,換了誰是老板都不愿意這種事情發生。
    • 開發人員呢?誰愿意放棄和家人朋友而拼死拼活在單位加班,總是有這么多麻煩事纏著你,煩不煩哪!
    • ……等等

    所以,設計應該盡可能多地照顧到維護和變更。

    為了兼顧各戶滿意和維護成本,設計應該不斷挑戰其終極目標——松耦合。不管是XP或UP,這個目標都不會改變。OO設計中的五大原則,其根本目的就是降低組件間的耦合度,避免牽一發則動全身的現象發生。降低耦合度不僅能夠提高軟件內在的質量,還能大大減少不必要的編譯時間、減少向版本控制系統提交源碼的網絡開銷……

    如何鑒別設計的這一指標?軟件工程中有專用的度量:CBO(Coupling Between Objects),那是由公式計算出來的,也有很多工具支持,值得一試。(聽過幾次李維先生的講座,他經常拿Together的度量功能炫耀^_^)

    但是,作為一個開發人員,對手中的代碼應該有適當的敏感性。畢竟,這些代碼是你親手創造的,誰不希望自己的作品得到眾人的贊許?或許能換得一次加薪升職的機會^_^ 退一步,這可關系到寶貴的休息時間啊。所以,開發者應該對自己的產品有這樣一種意識:及時修正設計中不合理的地方。

    敏捷過程告訴我們:在代碼“有味道”的時候進行重構?!坝形兜馈笔谴a正在變質的標志,很遺憾,能夠使代碼保持原味的防腐劑還沒發明。為了保證代碼質量,及時重構是必要的。這就像在燒烤的時候為了防止烤焦,你得坐在爐子前經常翻動肉塊一樣。

    如何聞出代碼的味道?認真學習一下OO吧,別以為OO很簡單,就是繼承+封裝+多態,誰都會。即使是書中記述的五大原則,想要運用自如,也得多感覺感覺才行。很多時候,我們不知不覺就把蛆蟲放進了代碼中……

    好了,下一篇:OO五大原則

    posted @ 2006-01-06 18:17 GHawk 閱讀(1595) | 評論 (3)編輯 收藏

    敏捷軟件開發 讀書筆記 (序——廢話)

    7-5083-1503-0l.gif

    最近正在讀這本書,喜歡影印版,是因為書中漂亮的插圖。:)慚愧,如此的好書到現在才去讀。
    準備邊讀邊記錄些心得,今天先說些廢話。:P

    先粗略地概覽了一遍全書。本書主要分以下幾個部分:

    1. 敏捷軟件過程。主要以XP為例。這部分的最后一章,用一個對話式的小故事講述了一個非常小的過程。給了讀者關于敏捷過程的形象化的認識。
    2. 敏捷設計。這部分是個很大的看點。它講述了設計中一些常見的問題,及其應對(用幾個經典的設計原則)。
    3. 案例實踐。講述了如何利用設計模式去實踐第二部分中提到的設計原則和避免設計中的“味道”。

    之所以覺得這本書好,還與一個人有關。就是交大軟件學院的林德彰老師。林先生的課,風趣幽默,能夠用直觀形象的語言讓學生對講課內容產生深刻的印象。(我可不是托兒,網上能搜到些林先生講課的片斷,要是懷疑,可以驗證一番)。記得在軟件工程這門課里,林先生給我們講了很多有關設計原則的內容,其中就有“開閉原則(OCP)”、“里氏替換原則(LSP)”等……就把這本書當作是一本補充讀物吧。

    言歸正傳。個人感覺這本書的總體風格,就和所要講的“敏捷”一樣,并不帶著厚重的學院派風味,而是更注重實踐。并不是沒有理論,只是把理論融入到了實踐中,簡化了理論的復雜性。讀起來感覺很帶勁兒。

    廢話說到這里,下一步的計劃就是跟著自己的進度寫讀書心得了。我想把對書中內容的理解和以前在林先生的課上所學的結合在一起,導出閱讀此書時的大腦活動鏡像。

    posted @ 2005-12-27 12:00 GHawk 閱讀(1611) | 評論 (4)編輯 收藏

    一個介紹Java開源項目及工具的網站

    按功能進行了歸類
    http://java-source.net


    再追加一個中文的
    http://www.open-open.com

    posted @ 2005-12-20 13:45 GHawk 閱讀(510) | 評論 (1)編輯 收藏

    用 Cobertura 測量測試覆蓋率

    http://www-128.ibm.com/developerworks/cn/java/j-cobertura/

    用 Cobertura 測量測試覆蓋率

    找出隱藏 bug 的未測試到的代碼

    developerWorks
    文檔選項
    將此頁作為電子郵件發送

    將此頁作為電子郵件發送

    未顯示需要 JavaScript 的文檔選項


    對此頁的評價

    幫助我們改進這些內容


    級別: 初級

    Elliotte Rusty Harold, 副教授, Polytechnic University

    2005 年 5 月 26 日

    Cobertura 是一種開源工具,它通過檢測基本的代碼,并觀察在測試包運行時執行了哪些代碼和沒有執行哪些代碼,來測量測試覆蓋率。除了找出未測試到的代碼并發現 bug 外,Cobertura 還可以通過標記無用的、執行不到的代碼來優化代碼,還可以提供 API 實際操作的內部信息。Elliotte Rusty Harold 將與您分享如何利用代碼覆蓋率的最佳實踐來使用 Cobertura。

    盡管測試先行編程(test-first programming)和單元測試已不能算是新概念,但測試驅動的開發仍然是過去 10 年中最重要的編程創新。最好的一些編程人員在過去半個世紀中一直在使用這些技術,不過,只是在最近幾年,這些技術才被廣泛地視為在時間及成本預算內開發健壯的無缺陷軟件的關鍵所在。但是,測試驅動的開發不能超過測試所能達到的程度。測試改進了代碼質量,但這也只是針對實際測試到的那部分代碼而言的。您需要有一個工具告訴您程序的哪些部分沒有測試到,這樣就可以針對這些部分編寫測試代碼并找出更多 bug。

    Mark Doliner 的 Cobertura (cobertura 在西班牙語是覆蓋的意思)是完成這項任務的一個免費 GPL 工具。Cobertura 通過用額外的語句記錄在執行測試包時,哪些行被測試到、哪些行沒有被測試到,通過這種方式來度量字節碼,以便對測試進行監視。然后它生成一個 HTML 或者 XML 格式的報告,指出代碼中的哪些包、哪些類、哪些方法和哪些行沒有測試到??梢葬槍@些特定的區域編寫更多的測試代碼,以發現所有隱藏的 bug。

    閱讀 Cobertura 輸出

    我們首先查看生成的 Cobertura 輸出。圖 1 顯示了對 Jaxen 測試包運行 Cobertura 生成的報告(請參閱 參考資料)。從該報告中,可以看到從很好(在 org.jaxen.expr.iter 包中幾乎是 100%)到極差(在 org.jaxen.dom.html 中完全沒有覆蓋)的覆蓋率結果。


    圖 1. Jaxen 的包級別覆蓋率統計數據

    Cobertura 通過被測試的行數和被測試的分支數來計算覆蓋率。第一次測試時,兩種測試方法之間的差別并不是很重要。Cobertura 還為類計算平均 McCabe 復雜度(請參閱 參考資料)。

    可以深入挖掘 HTML 報告,了解特定包或者類的覆蓋率。圖 2 顯示了 org.jaxen.function 包的覆蓋率統計。在這個包中,覆蓋率的范圍從 SumFunction 類的 100% 到 IdFunction 類的僅為 5%。


    圖 2. org.jaxen.function 包中的代碼覆蓋率

    進一步深入到單獨的類中,具體查看哪一行代碼沒有測試到。圖 3 顯示了 NameFunction 類中的部分覆蓋率。最左邊一欄顯示行號。后一欄顯示了執行測試時這一行被執行的次數??梢钥闯?,第 112 行被執行了 100 次,第 114 行被執行了 28 次。用紅色突出顯示的那些行則根本沒有測試到。這個報告表明,雖然從總體上說該方法被測試到了,但實際上還有許多分支沒有測試到。


    圖 3. NameFunction 類中的代碼覆蓋率

    Cobertura 是 jcoverage 的分支(請參閱 參考資料)。GPL 版本的 jcoverage 已經有一年沒有更新過了,并且有一些長期存在的 bug,Cobertura 修復了這些 bug。原來的那些 jcoverage 開發人員不再繼續開發開放源碼,他們轉向開發 jcoverage 的商業版和 jcoverage+,jcoverage+ 是一個從同一代碼基礎中發展出來的封閉源代碼產品。開放源碼的奇妙之處在于:一個產品不會因為原開發人員決定讓他們的工作獲得相應的報酬而消亡。

    確認遺漏的測試

    利用 Cobertura 報告,可以找出代碼中未測試的部分并針對它們編寫測試。例如,圖 3 顯示 Jaxen 需要進行一些測試,運用 name() 函數對文字節點、注釋節點、處理指令節點、屬性節點和名稱空間節點進行測試。

    如果有許多未覆蓋的代碼,像 Cobertura 在這里報告的那樣,那么添加所有缺少的測試將會非常耗時,但也是值得的。不一定要一次完成它。您可以從被測試的最少的代碼開始,比如那些所有沒有覆蓋的包。在測試所有的包之后,就可以對每一個顯示為沒有覆蓋的類編寫一些測試代碼。對所有類進行專門測試后,還要為所有未覆蓋的方法編寫測試代碼。在測試所有方法之后,就可以開始分析對未測試的語句進行測試的必要性。

    (幾乎)不留下任何未測試的代碼

    是否有一些可以測試但不應測試的內容?這取決于您問的是誰。在 JUnit FAQ 中,J. B. Rainsberger 寫到“一般的看法是:如果 自身 不會出問題,那么它會因為太簡單而不會出問題。第一個例子是 getX() 方法。假定 getX() 方法只提供某一實例變量的值。在這種情況下,除非編譯器或者解釋器出了問題,否則 getX() 是不會出問題的。因此,不用測試 getX(),測試它不會帶來任何好處。對于 setX() 方法來說也是如此,不過,如果 setX() 方法確實要進行任何參數驗證,或者說確實有副作用,那么還是有必要對其進行測試。”

    理論上,對未覆蓋的代碼編寫測試代碼不一定就會發現 bug。但在實踐中,我從來沒有碰到沒有發現 bug 的情況。未測試的代碼充滿了 bug。所做的測試越少,在代碼中隱藏的、未發現的 bug 就會越多。

    我不同意。我已經記不清在“簡單得不會出問題”的代碼中發現的 bug 的數量了。確實,一些 getter 和 setter 很簡單,不可能出問題。但是我從來就沒有辦法區分哪些方法是真的簡單得不會出錯,哪些方法只是看上去如此。編寫覆蓋像 setter 和 getter 這樣簡單方法的測試代碼并不難。為此所花的少量時間會因為在這些方法中發現未曾預料到的 bug 而得到補償。

    一般來說,開始測量后,達到 90% 的測試覆蓋率是很容易的。將覆蓋率提高到 95% 或者更高就需要動一下腦筋。例如,可能需要裝載不同版本的支持庫,以測試沒有在所有版本的庫中出現的 bug。或者需要重新構建代碼,以便測試通常執行不到的部分代碼??梢詫︻愡M行擴展,讓它們的受保護方法變為公共方法,這樣就可以對這些方法進行測試。這些技巧看起來像是多此一舉,但是它們曾幫助我在一半的時間內發現更多的未發現的 bug。

    并不總是可以得到完美的、100% 的代碼覆蓋率。有時您會發現,不管對代碼如何改造,仍然有一些行、方法、甚至是整個類是測試不到的。下面是您可能會遇到的挑戰的一些例子:

    • 只在特定平臺上執行的代碼。例如,在一個設計良好的 GUI 應用程序中,添加一個 Exit 菜單項的代碼可以在 Windows PC 上運行,但它不能在 Mac 機上運行。

    • 捕獲不會發生的異常的 catch 語句,比如在從 ByteArrayInputStream 進行讀取操作時拋出的 IOException。

    • 非公共類中的一些方法,它們永遠也不會被實際調用,只是為了滿足某個接口契約而必須實現。

    • 處理虛擬機 bug 的代碼塊,比如說,不能識別 UTF-8 編碼。

    考慮到上面這些以及類似的情況,我認為一些極限程序員自動刪除所有未測試代碼的做法是不切實際的,并且可能具有一定的諷刺性。不能總是獲得絕對完美的測試覆蓋率并不意味著就不會有更好的覆蓋率。

    然而,比執行不到的語句和方法更常見的是殘留代碼,它不再有任何作用,并且從代碼基中去掉這些代碼也不會產生任何影響。有時可以通過使用反射來訪問私有成員這樣的怪招來測試未測試的代碼。還可以為未測試的、包保護(package-protected)的代碼來編寫測試代碼,將測試類放到將要測試的類所在那個包中。但最好不要這樣做。所有不能通過發布的(公共的和受保護的)接口訪問的代碼都應刪除。執行不到的代碼不應當成為代碼基的一部分。代碼基越小,它就越容易被理解和維護。

    不要漏掉測量單元測試包和類本身。我不止一次注意到,某些個測試方法或者類沒有被測試包真正運行。通常這表明名稱規范中存在問題(比如將一個方法命名為 tesSomeReallyComplexCondition,而不是將其命名為 testSomeReallyComplexCondition),或者忘記將一個類添加到主 suite() 方法中。在其他情況下,未預期的條件導致跳過了測試方法中的代碼。不管是什么情況,都是雖然已經編寫了測試代碼,但沒有真正運行它。JUnit 不會告訴您它沒有像您所想的那樣運行所有測試,但是 Cobertura 會告訴您。找出了未運行的測試后,改正它一般很容易。



    回頁首


    運行 Cobertura

    在了解了測量代碼覆蓋率的好處后,讓我們再來討論一下如何用 Cobertura 測量代碼覆蓋率的具體細節。Cobertura 被設計成為在 Ant 中運行。現在還沒有這方面的 IDE 插件可用,不過一兩年內也許就會有了。

    首先需要在 build.xml 文件中添加一個任務定義。以下這個頂級 taskdef 元素將 cobertura.jar 文件限定在當前工作目錄中:

    <taskdef classpath="cobertura.jar" resource="tasks.properties" />

    然后,需要一個 cobertura-instrument 任務,該任務將在已經編譯好的類文件中添加日志代碼。todir 屬性指定將測量類放到什么地方。fileset 子元素指定測量哪些 .class 文件:

    <target name="instrument">
      <cobertura-instrument todir="target/instrumented-classes">
        <fileset dir="target/classes">
          <include name="**/*.class"/>
        </fileset>
      </cobertura-instrument>
    </target>

    用通常運行測試包的同一種類型的 Ant 任務運行測試。惟一的區別在于:被測量的類必須在原始類出現在類路徑中之前出現在類路徑中,而且需要將 Cobertura JAR 文件添加到類路徑中:

    <target name="cover-test" depends="instrument">
      <mkdir dir="${testreportdir}" />
      <junit dir="./" failureproperty="test.failure" printSummary="yes" 
             fork="true" haltonerror="true">
        <!-- Normally you can create this task by copying your existing JUnit
             target, changing its name, and adding these next two lines.
             You may need to change the locations to point to wherever 
             you've put the cobertura.jar file and the instrumented classes. -->
        <classpath location="cobertura.jar"/>
        <classpath location="target/instrumented-classes"/>
        <classpath>
          <fileset dir="${libdir}">
            <include name="*.jar" />
          </fileset>
          <pathelement path="${testclassesdir}" />
          <pathelement path="${classesdir}" />
        </classpath>
        <batchtest todir="${testreportdir}">
          <fileset dir="src/java/test">
            <include name="**/*Test.java" />
            <include name="org/jaxen/javabean/*Test.java" />
          </fileset>
        </batchtest>
      </junit>
    </target>>

    Jaxen 項目使用 JUnit 作為其測試框架,但是 Cobertura 是不受框架影響的。它在 TestNG、Artima SuiteRunner、HTTPUni 或者在您自己在地下室開發的系統中一樣工作得很好。

    最后,cobertura-report 任務生成本文開始部分看到的那個 HTML 文件:

    <target name="coverage-report" depends="cover-test">
     <cobertura-report srcdir="src/java/main" destdir="cobertura"/>
    </target>

    srcdir 屬性指定原始的 .java 源代碼在什么地方。destdir 屬性指定 Cobertura 放置輸出 HTML 的那個目錄的名稱。

    在自己的 Ant 編譯文件中加入了類似的任務后,就可以通過鍵入以下命令來生成一個覆蓋報告:

    % ant instrument
    % ant cover-test
    % ant coverage-report

    當然,如果您愿意的話,還可以改變目標任務的名稱,或者將這三項任務合并為一個目標任務。



    回頁首


    結束語

    Cobertura 是敏捷程序員工具箱中新增的一個重要工具。通過生成代碼覆蓋率的具體數值,Cobertura 將單元測試從一種藝術轉變為一門科學。它可以尋找測試覆蓋中的空隙,直接找到 bug。測量代碼覆蓋率使您可以獲得尋找并修復 bug 所需的信息,從而開發出對每個人來說都更健壯的軟件。



    回頁首


    參考資料



    回頁首


    關于作者

    Elliotte Rusty Harold 出生在新奧爾良,現在他還定期回老家喝一碗美味的秋葵湯。不過目前,他和妻子 Beth 定居在紐約臨近布魯克林的 Prospect Heights,同住的還有他的貓咪 Charm(取自夸克)和 Marjorie(取自他岳母的名字)。他是 Polytechnic 大學計算機科學的副教授,講授 Java 技術和面向對象編程。他的 Cafe au Lait 網站是 Internet 上最受歡迎的獨立 Java 站點之一,姊妹站點 Cafe con Leche 已經成為最受歡迎的 XML 站點之一。他的著作包括 Effective XML 、 Processing XML with Java 、 Java Network Programming The XML 1.1 Bible 。目前他正在從事 XML 的 XOM API、Jaxen XPath 引擎和 Jester 測試覆蓋率工具的開發工作。


    posted @ 2005-12-17 11:23 GHawk 閱讀(423) | 評論 (0)編輯 收藏

    EasyMock 2.0_ReleaseCandidate 文檔翻譯

         摘要: EasyMock 2.0_ReleaseCandidate Readme Documentation for release 2.0_ReleaseCandidate (October 15 2005)? 2001-2005 OFFIS, Tammo Freese. 翻譯:GHawk, 2005-12-15 EasyMock 2 is a library that provides an ...  閱讀全文

    posted @ 2005-12-15 16:06 GHawk 閱讀(4763) | 評論 (2)編輯 收藏

    過程的代價

    這個月剛進入公司,加入了一個10人左右的團隊,用Java做一個網站后臺。

    客戶是日本公司,他們做了項目的大部分分析(Requirements, Use cases, Domain model...)。我們負責的是詳細設計和開發。我是項目開始幾星期后才進的公司。Schedule也已經為我分配好了。大家都按照schedule上的安排工作著。

    上星期開會的時候得知,日本這次采用的是agile過程。而我們的schedule更類似于RUP這樣的過程。RUP這個學院派和Agile這個造反派狹路相逢,問題也就出現了。

    大家工作都很賣力,為了能按進度提交制品,有時還通宵達旦解決問題。我們這支團隊的戰斗力和信心是不容懷疑的??墒谴蠹遗Φ慕Y果換來的卻是用戶的抱怨。大家都困惑不解。問題究竟出在哪兒?

    日方在項目中強調的是Agile過程,我們采用的則是傳統的過程。一開始,兩個過程方法之間的差異并不大;對我們提交的制品,客戶也沒有什么異議。但是,直到客戶提出問題之前,我們所提交的制品都是一些設計文檔。而我們的制品也僅限于此——沒有一個可用的EAR包、沒有寫過 test case。很明顯,我們犯了agile的大忌。

    Agile所強調的是快速的構建、輕量級的迭代、TDD等。由于之前沒有寫test case,詳細設計也沒有test case可以參照。設計本身是不是合理,是不是testable也不得而知。致使在設計test case的時候無從下手,很多類甚至都沒有辦法測試。整個架構的可行性很難估算。

    往后考慮。一次大規模的重構可能是少不了的。雖然agile過程本身提倡以TDD為基礎的重構。但是現在的重構可能造成的代價已經不是一次輕量級的增量迭代了。

    說到這里,總結幾點,希望能在以后的工作中引起注意:
    1. Agile很難管理,項目早期應該對各種風險有盡可能全面的評估,schedule的設置中應該定義好 test case 和 build 的時間點。
    2. 設計不必太詳細,用頻繁的測試和重構完善設計。
    3. Test case 優先設計,這樣在架構中就會對testability有足夠多的考慮。
    4. 團隊內部對共同的難題應該及早進行討論和解決,問題的解決方案應該傳遞到每個組員,盡可能保證團隊的能力同步。

    posted @ 2005-12-14 09:57 GHawk 閱讀(1200) | 評論 (5)編輯 收藏

    僅列出標題
    共3頁: 上一頁 1 2 3 下一頁 
    主站蜘蛛池模板: 亚洲人成片在线观看| 亚洲色成人网站WWW永久| 永久免费无码网站在线观看个| 久久影视综合亚洲| 国国内清清草原免费视频99| 日韩在线观看免费完整版视频| 精品国产亚洲第一区二区三区| 亚洲AV无码成人专区片在线观看| 黄色成人网站免费无码av| 国产精品1024在线永久免费 | a级毛片黄免费a级毛片| 国产精品亚洲四区在线观看| 亚洲偷自拍拍综合网| 成年美女黄网站18禁免费| 日本免费中文视频| 日韩欧美亚洲中文乱码| 亚洲国产美女在线观看 | 天天干在线免费视频| 无码人妻AV免费一区二区三区| 美国免费高清一级毛片| 亚洲av永久无码嘿嘿嘿| 亚洲国产精品一区二区久久| 亚洲人成无码网WWW| 伊伊人成亚洲综合人网7777| 亚洲AⅤ永久无码精品AA| 国产免费AV片在线播放唯爱网| 一区二区三区观看免费中文视频在线播放 | 任你躁在线精品免费| 黄色三级三级免费看| 久久久久亚洲国产| 内射干少妇亚洲69XXX| 在线观看亚洲天天一三视| 亚洲国产精华液网站w| 亚洲毛片不卡av在线播放一区| 免费鲁丝片一级在线观看| 国产福利视精品永久免费| 色欲色香天天天综合网站免费| 四虎1515hh永久久免费| 18pao国产成视频永久免费| 久久免费美女视频| a级精品九九九大片免费看|