在第十三次做業中,咱們使用了JUnit單元測試框架對咱們在第三次做業中編寫的捎帶電梯程序中的每個方法進行了測試。捎帶電梯程序在通過了互測以後,已是一個比較完善的系統,可是經過大量細緻的單元測試,仍能從程序中尋找到一些邏輯漏洞甚至錯誤。經過觀察每一個方法的語句和分支覆蓋率,能夠比較直觀地看到本身的代碼是否每一行都有其價值。在寫第十三次做業的過程當中,我不止一次遇到了這樣的狀況:在針對某一個複雜的方法設計大量測試用例,甚至用程序自動生成隨機測試樣例,都沒法對某一條語句或者某一個分支作到100%的覆蓋。這個時候,我就會從新檢查這個方法的實現邏輯,判斷這條語句或者這個分支是否真的有存在的必要。很顯然,細緻的單元測試有助於咱們優化和完善本身的代碼。算法
而在第十四次做業中,咱們採起了正確性論證的方法對同一個捎帶電梯程序進行了正確性分析。經過對JSF的後置條件進行劃分,將一個方法所須要處理的全部狀況分爲幾種小的分支,而後對這些小分支逐一審視代碼的實現是否知足了JSF的後置條件。在JSF後置條件明確且可驗證的基礎上,正確性論證可使咱們從新審視本身的代碼,細緻地分析其實現邏輯,並確認本身是否覆蓋了每一種可能的狀況。因爲第十四次做業是在第十三次做業完善後的程序之上完成的,所以正確性論證沒有幫助我找到代碼中的邏輯漏洞;但它驅使我完善本身的JSF後置條件,而且對規模較大的方法進行拆解或重構,以達到可進行正確性論證的目的。從這個角度上來說,正確性論證進一步優化了程序的可驗證性。編程
單元測試和正確性論證的本質差別其實用一句話就能夠歸納:單元測試是黑盒測試,而正確性論證是白盒測試。 安全
在構造單元測試的測試樣例時,測試者能夠徹底不知道程序的具體實現細節,機械化地生成測試數據,並根據方法的規格判斷方法的實現是否正確。它的好處是簡單粗暴快速有效,通過大量測試數據的轟炸,代碼中最細微的漏洞也能夠被揪出來並得以修正。此外,單元測試對代碼的重構具備奇效。若是沒有自動化的單元測試,重構代碼的時候就須要不停地去手動驗證以前的測試樣例,一旦發現問題,修改以後又要重頭跑,既浪費時間,又容易出錯。單元測試有助於程序設計者以自動化的方式去驗證程序的正確性,這是正確性論證所不具有的優勢。可是,單元測試也有其缺陷:因爲它是黑盒測試,從機率的角度上講,不管測試數據再怎麼完善,總會有漏網之魚的Bug出現。換句話說,在程序有一個良好的設計且規格完善的前提下,單元測試能夠保證程序的正確率達到99%以上,但沒法確保100%正確。多線程
正確性論證與單元測試不一樣,它是白盒測試,這意味着測試者須要深刻代碼的實現,去逐個論證明現與規格上的不一樣之處。這個過程比較繁瑣,並且對重構極不友好,一旦代碼發生較大的變更,許多方法的正確性論證就要重寫,形成不少額外的麻煩。可是,這些麻煩換來的好處就是,一個被正確設計了的且規格完善的程序只要完成了正確性論證,就能夠從邏輯上確保100%的實現正確性,其保證力度要大於單元測試的保證力度。所以,對於一些再也不須要進行修改的方法,進行正確性論證是比單元測試更爲穩妥的選擇。框架
在實際的工程中,我認爲應以單元測試爲主,正確性論證爲輔。由於需求老是在不斷變更的,單元測試的簡便和高效適合更現代軟件工程的開發流程。而對於那些比較重要的核心算法代碼(即不會隨需求更改而變更的代碼),應進行正確性論證,以確保邏輯上的100%準確。工具
值得注意的是,不管是單元測試仍是正確性論證,其有效的前提都是程序的設計是合理且正確的,各個類和方法的規格也都已經完善。沒有規格的代碼就像沒有標準答案的考試卷,學生答起來羣魔亂舞,老師判起來無從下手。單元測試和正確性論證都是必要的,但這並不表明有了它們就能夠萬事大吉。在軟件開發環節的最開始就作出合理的設計,而且撰寫好相應的規格,單元測試和正確性論證才能發揮其最大做用,爲提升程序的正確性帶來價值。性能
OCL語言是約束(Constriant)語言和查詢(Query)語言。一個約束就是對一個(或部分)面向對象模型或者系統的一個或者一些值的限制,UML類圖中的全部值均可以被約束,而表達這些約束的方法就是OCL語言。在UML2標準中,OCL語言不只可以用來寫約束,還可以用來對UML類圖中的任何元素寫表達式,每一個OCL表達式都能指出系統中的一個值或者對象。由於OCL表達式可以求出一個系統中的任何值或者值的集合,所以它具備了和SQL一樣的能力,於是OCL也是一種查詢語言。單元測試
OCL語言的基礎是數學中的集合論和謂詞邏輯,而且它有一個形式化的數學語義,可是它並無使用某種數學符號。由於雖然數學符號可以清晰的、無歧義的表達事物,可是隻有極少的專家能夠看懂。因此數學符號並不適合用於一個普遍應用的標準語言。天然語言是最易懂的,但它倒是含混不清晰的。OCL取了天然語言和數學符號的折中方案,使用普通的ASCII字符來表達數學中一樣的概念。學習
OCL是一個類型語言,任何表達式的值都是屬於一個類型的。這個類型能夠是預約義的標準類型例如Boolean或者Integer,也能夠是UML圖中的元素例如對象。也能夠是這些元素組成的集合,例如對象的集合、包、有序集合等等。測試
OCL是一種聲明式(Declarative)語言,表達式僅僅描述了應該去作"什麼",而不是應該"怎樣"去作。由於OCL是聲明式語言,因此UML中的表達式被提高到了純建模的領域,而沒必要理會實現的細節和實現的語言。
OCL起源於1997年BIM公司爲響應OMG的"面向對象分析和設計標準"徵求稿所提交的"對象時間限制提議",OCL是該提議的部份內容。用OCL能夠描述四類約束,分別是不變量、前置條件、後置條件和監護條件:
1)不變量是在屬性的生命期內一直保持爲真的規則。
2)前置條件是在一個操做被調用時必須爲真的約束。它是一個斷言,不是可執行語句。
3)後置條件就是在操做完成時必須爲真的約束。它不是可執行語句而是斷言,必須爲真。
4)監護規則是在對象可以從一種狀態轉變爲另外一種狀態前其值必須爲真的約束。
每個OCL表達式都必須賦予一個明確的上下文來定義參考基準。在模型中的任何一個元素均可以定義爲一個上下文,例如類、屬性、操做和關聯。一旦咱們定義了上下文,就能夠開始定義約束表達式餓。OCL是一種聲明式語言,大部分表達式執行後會返回一個布爾值,也有一些表達式會用來選擇一個單一值或者一個對象/值的集合。
能夠看到,OCL語言和咱們在面向對象課程中所學到的JSF具備類似之處。不變量、前置條件、後置條件這些咱們已經耳熟能詳的概念在OCL和JSF中都有體現,其所表明的含義也都大體相同。OCL中的監護條件則有點相似於JSF中的repOK方法(但並不徹底一致),即系統狀態只要知足相關要求,就能夠進行任意知足規格的調用。經過對這些約束的斷言,咱們得以判斷一個方法是否被正確調用或者是否被正確實現,至關於爲一張空白的試卷制定了規則和答案。有了這些規則和答案,咱們再去具體撰寫代碼實現的時候,就有了相應的依據,能夠自行判斷出實現是否正確。此外,它們還能夠幫助咱們撰寫高質量的單元測試和正確性論證。
不一樣之處在於,OCL語言是基於UML類圖的,而JSF是基於代碼自己的。從嚴謹性程度上來說,JSF也更高一籌,由於正如上文所說,布爾表達式(以集合論和謂詞邏輯爲基礎)是最嚴謹的表達,其帶來的約束的嚴謹性遠勝於天然語言,且適合進行自動化驗證。可是,形式化的數學語言並不適合全部人閱讀,並且一些較爲複雜的邏輯能夠用簡單的天然語言描述出來,但絕對沒法用簡單的數學語言去描述。所以,OCL和JSF各有其適用範圍,二者的缺點正好是對方的優勢。在工程開發中,兩者互補爲佳。
第一單元有三次做業:多項式加減、單傻瓜電梯、單捎帶電梯。第一次做業的難度極小,主要是爲了讓同窗們初步接觸Java編程,並將思惟從C語言的面向過程轉爲Java的面向對象。第二次做業的單傻瓜電梯稍有難度,相比於多項式加減,同窗們須要設計並實現的類變多了,大部分同窗在此次做業中第一次體驗到了多個類協做帶來的面向對象編程體驗。第三次做業的難度陡然增長,指導書變得複雜了許多,算法也比較難以實現。尤爲是同窗們在寫單電梯的時候還須要考慮以後的多線程電梯,所以寫的時候當心翼翼、舉步維艱,生怕後面還須要重構(然而事實證實,多線程電梯仍是須要大規模重構…)。
第二單元能夠說是整個課程體系中最難的一個單元。多線程的首次引入、指導書的繁雜、調試的不便、互測的博弈,這些元素使得這三次做業的難度陡然增長。第五次做業的多線程電梯中,同窗們須要經過多線程完成三部電梯的協做,並在捎帶算法的基礎上增長最小運動量原則。這些算法和以前的差異不大,難點在於多線程對調度器實現方法的影響。第六次做業是文件監視器,是指導書改動最頻繁的一次,絕對難度並不大,複雜之處在於仔細理解指導書,並完成一個線程安全的設計。第七次做業的出租車調度是一系列做業的開始,此次做業中首次引入了設計原則,經過知足這些設計原則,同窗們能夠在以後的增量設計中取得工做量上的減輕。
第三單元主打JSF。第9、10、十一次做業按部就班,逐步引入方法規格、類規格、帶有繼承的類規格,讓同窗們能夠經過完善規格,掌握作出一個良好設計的方法。這三次做業的難度不大、算法簡單,可是要想寫好JSF仍是須要下必定的功夫。
第四單元是測試與論證。一個程序若是沒有正確性,那就不配稱之爲一個程序,第十三次做業的JUnit單元測試和第十四次做業的正確性論證是兩種增長程序正確性的方法,經過這兩次做業對第三次做業單捎帶電梯的檢查,同窗們基本掌握了測試和驗證本身程序的方法,這種方法在以後的編程生涯中大有裨益。
這四個單元有一個清晰的主線:熟悉面向對象 à 多線程編程 à 設計與規格 à 測試與驗證。這條主線是從一個對面向對象徹底沒有概念的編程新手到能寫出1000行具備優良設計風格的面向對象代碼的人所必經的學習道路。四個單元之間按部就班,卻又藕斷絲連,對同窗們而言有着很大的訓練價值。
在面向對象課程中,個人主要進步能夠用一句話來歸納:從拿到需求(題目)就開始無腦寫代碼,變成了先思考再編碼。我以爲大二整個課程體系(包括但不限於計算機組成、面向對象)都是在訓練咱們設計與實現分離的編程風格,事實上通過這一年的訓練,個人設計能力和編碼能力確實有了長足的提升。
此外,還有一些小的方面的進步。例如,之前我從未接觸過多線程編程,也沒有使用過Java的反射,面向對象課程的做業讓我熟悉並掌握了這些頗有用的編程工具。
我並無參與過真正的工程化開發,所以接下來這一小節的內容都是基於個人想象。
我理解的工程化開發,重點在於協做。現代大型的軟件工程規模已經大到了絕對不可能僅靠一我的單槍匹馬就能完成開發,所以多人協做就顯得尤其重要。當許多人一塊兒完成一個大型項目的時候,因爲每一個成員的能力不一樣、對項目的理解也不一樣,所以如何進行良好的溝通(包括語言層面的溝通和代碼層面的溝通)成爲了一個很大的問題。面向對象這門課程所教授的規格化設計就是解決溝通問題而進行的一個嘗試。類的設計者經過規定前置規格來約定使用者的輸入參數範圍,經過撰寫後置規格來提示使用者類和方法調用後的做用;類的使用者經過遵循前置規格來得到知足後置規格的調用結果,經過反覆調用repOK方法得到類是否可以正常工做的反饋。
"協做"這個詞的內涵是豐富的。爲了溝通方便,完善的註釋和優秀的代碼風格是必需的,各類設計原則的知足是必須的,規範撰寫的過程規格也是必需的。若是沒有這些,一我的沒法理解另外一我的的想法,沒法對代碼做出修改和重構,即便是代碼撰寫者本身,也會在一段時間以後,忘記以前代碼的設計思路。所以,在工程化開發中,爲了溝通和協做的須要,更多的時間應該花在規劃、設計和測試上,真正的編碼實現只佔整個工程的一小部分。
工程化開發和本身作一個小項目徹底不同。在本身的項目中,任性沒有關係,甚至有時候任性可以來帶來更好的創意;然而在工程化開發中,任性只能給整個團隊帶來災難。
但願可以取消後面三次的出租車做業(即第三單元的三次以JSF爲主要訓練目的的做業),改成一個從零開始的、先撰寫規格再完善代碼的按部就班的項目。在現有的課程體系中,同窗們接觸JSF的方式是經過先寫代碼再補充規格的方式,這使得大部分同窗難以體會到規格化設計對於一個工程項目的重要性。若是可以經過一個系列做業,讓同窗們完總體會先設計再實現的過程,想必"設計無用論"的想法會少不少。
此外,在互測制度上,建議再完善一下JSFTools,爲JSF制定一個統一的規範標準,以免互測雙方對JSF標準的理解不一樣而引起的爭論。
最後,但願面向對象課程能變得愈來愈好,而不是讓愈來愈多的人去討厭。