程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 診斷Java代碼: 在規范鋼絲上行走

診斷Java代碼: 在規范鋼絲上行走

編輯:關於JAVA

要構建可靠的軟件,程序規范很關鍵。沒有良好定義的規范,很難診斷軟件系統的異常行為。但是很多軟件系統的程序規范定義得很差勁。而且更糟的,是許多軟件系統根本就沒有規范。

直觀的看,程序規范是對程序行為的一種描述。它可以采取許多形式,但無論采取何種形式,都有一條主線貫穿所有實例:必須有某種類型的系統規范,因為您得依靠它來判斷系統是否運轉正常。

規范可以形式化也可以松散地定義,這取決於開發中系統的穩定性和危險程度,還與開發完畢後修改系統的容易程度有關。

我們將通過討論規范為什麼重要、為什麼會經常被忽略以及如何改善這種情況來開始我們的這次“旅程”。

平衡精度的成本和收益

在微處理器設計世界中,系統部署在各種規模的應用上,從個人計算機到擔負重任的醫療和軍事系統。在這個領域中有一個普遍的、牢不可破的規則:在部署之後修改芯片設計的成本是極其昂貴的。

因此,通常會將微處理器的規范形式化,這也就不足為奇了。一個正式的規范有巨大的好處,因為能夠自動地解釋和分析它。就微處理器而言,設計的許多方面可以自動地被驗證無誤。

軟件類比:編程語言

在軟件世界中,在部署和危險程度方面,最類似於微處理器的事物是編程語言。一種普及的編程語言被用來編寫無數的程序,它們被用於各種危險程度級別不同的系統中。

和芯片一樣,在投入使用之後修改語言設計的成本是相當昂貴的,因為所有現有的程序都必須修改和重新編譯。因此,與其它軟件系統相比,編程語言的規范通常是相當正式的。

對於語法,這種形式化尤其重要。事實上,所有現代編程語言都擁有正式定義的語法。大多數解析器是通過使用自動的解析器生成器構造的,生成器讀入這些文法並產生完整的解析器作為輸出。

不幸的是,語言語義並不傾向於作這樣嚴格的規定。這並非因為不能做到這樣嚴格。

象 ML 這樣語言擁有形式化的語義,並因此證明了許多關於它們的法則,用於驗證其正確性的某些方面(譬如,它們的類型系統的完善)。但是象 ML 這樣的語言是特例。我們可以確定造成這種情況的兩個原因。

首先,因為證明關於編程語言規范的特性實際上比硬件設計更難以駕馭,所以不太需要正式的規范。相反,許多語言是用敘述性文字規定的。這些文字規范對於大多數實際使用語言的人(譬如編譯器作者)來說已經足夠了。實際上,編譯器作者通常著迷於不太正式的規范,因為它給了他們更多優化程序的空間。另外,有些時候語言的使用者是程序員,他們中的大多數很欣賞非正式規范,因為這樣他們可以輕松地理解規范。

第二個理由是,許多語言是由單獨的開發人員作為“業余愛好”開發的,而他們往往並不專長於編程語言領域。遺憾的是,這些開發人員常常並不了解為規定編程語言語義而開發的形式體系。

模稜兩可的成本的示例

然而,語言規范中的模稜兩可或不一致造成的成本可能是相當昂貴的,會導致可移植性、可靠性的降低,甚至會造成安全性漏洞。通過研究一些當前廣泛使用的語言,可以發現它們的規范中相對的精確程度是如何影響它們的。

C++ 語言的規范有許多模稜兩可的地方,甚至在語法級別也有。此外,規范的許多部分是變成依賴於實現的。結果是:C++ 程序通常很難在多種平台上按預期的那樣運行。

Python 語言規范遺留了許多依賴於實現或未定義的細節。結果,諸如 Jython 和 CPython 之類的實現,在提供與另一方相同的行為這個方面,面臨著巨大難題。如果不是因為 Python 語言相對比較簡單(並非貶義),這一問題還會更糟。

盡管 Java 語言沒有正式的規范(類似於 ML 那樣的),但是在精確的非正式規范的開發上投入了很多努力。該語言通常編譯成由 JVM 解釋的字節碼,而 JVM 本身具有良好的規范(盡管通過正式分析在該規范中發現了一些模稜兩可的地方)。此外,Java API 都作為 JVM 的一部分規定。這使得 Java 代碼具有空前程度的可移植性。

我們從中可以得出結論,一份盡可能精確的語言規范會非常有用。但即使在編程語言世界中,規范中的問題也是最昂貴的,精確的規范很少,部分原因在於預先制作一份精確規范很昂貴。

許多公司發現成本更低的做法是先交付產品,以後(或者很可能永遠也不會)再充實規范細節。誠然,對於生命周期較短以及部署范圍較窄的應用程序,預先定義精確規范確實是太昂貴了。有時可能競爭對手早已交付了系統,而開發團隊還未正式確定其系統的規范。

此外,大型規范很少在用戶需求更改時更新,並且因此被忽略了。但是如果預先定義規范太昂貴,開發團隊應該采取什麼方法來規定他們的軟件呢?

在回答這個問題之前,讓我們考慮一下一個常用的,但也確實是最糟糕的方法吧。

為什麼實現不是規范

與上面的方法相反,許多軟件直接實現,而沒有可用的規范。如果(或當)軟件完成,實現便作為規范提供。

換言之,他們將軟件展示的任何行為都說成是規定的行為。

有人可能會爭辯說這是一件好事,因為這樣防止了開發人員浪費時間去做出某種必定會更改的正式計劃。但是,盡管項目規范的確會經常更改,而實現在許多方面會導致一個糟糕的規范。下列是一些原因:

實現包含任意選項。

本質上,每個行為都是有意的,所以不會有錯誤。

實現中的許多選項是任意的。因而,任何將來在其它平台上實現它們的嘗試都只能從現有實現入手。開發人員將不得不花費很大精力,從眾多實現細節中確定實現需要的行為。如果在更高級的抽象中規定了這些行為,確定起來就容易得多了。

還有,如果將一個實現完全照搬為它自己的規范,那麼就不可能確定該實現的中的任何行為是錯誤。有多少次我們看到軟件公司將其軟件中有害的或討厭的行為標識為“正常”,而只是因為這是由於未開發規范所帶來的一個無法預料的結果?

劃算的規范

對於初始開發人員,實現無法有效充當規范的第三個顯而易見的原因是這樣的實現還不存在。這些開發人員必須依賴於他們正在創建的系統的某種行為模型,因此該模型的原始資料將充當軟件的規范。

關於開發人員應該使用哪種具有合理成本的規范,這一點給了我們一些啟示。但是實際上,為了確定如何實現一個功能部件,一個開發人員必須有一個關於該功能部件是什麼的思想模型,而不必擁有關於整個應用程序的思想模型。

換言之,規范可以分段開發。這不僅使它們更容易駕馭,還允許在客戶需求更改時更有效地修改它們。

在極端編程(XP)中,系統所需要的功能是在使用 素材的過程中逐步確定的。每個素材簡要描述系統行為的一個方面。例如,這裡有素材,可將它包含進一個 Java IDE 的規范中:

當用戶將文字輸入編輯器時,出現的 Java 關鍵字將自動顯示為藍色。字符串和字符文字顯示為綠色,注解顯示為紅色。

信不信由您,對於 Java 語言,這個素材實際上很難正確實現,部分原因在於塊注釋的某些特殊特性。根據開發團隊的速度,將該素材劃分成兩個或更多小素材或許是可取的。

但請注意,這個素材很小,而且是用簡單、清晰的語言寫出的。當需要時,這樣很容易拆分,並可以防止規范各部分之間的耦合。

此外,因為素材很小,所以它們可以更新,添加新素材而不必對規范作大的修改。出於這個原因,素材特別適合於那些在實現時經常要更改最終產品需求的業界設置。

不,單元測試也不是規范

在結束關於規范的這一主題之前,我們最好討論一下最後一個關於單元測試的問題。單元測試對於 XP 非常重要。程序員在編寫實現的任何部分之前就開始編寫它們,並繼續為功能的每個新的方面編寫更多的單元測試。

覆蓋軟件項目的一套嚴格的單元測試提供兩個巨大的好處:

他們可以是文檔化的。

它們加速了重構過程。

單元測試與靜態類型類似,是文檔的 可執行形式。因為單元測試理想地覆蓋了實現的所有方面,也因為它們用簡單的方式調用了功能以確定其是否正常運行,正在加入項目的或開始維護某些代碼的程序員只要通讀單元測試,就能夠容易地確定各種功能組件的用途。

許多人在第一次聽說單元測試可以是文檔這一概念時,會表示懷疑:“怎麼能用編寫程序的同一語言來編寫它的文檔呢?”但是這個問題忽略了代碼文檔。

不應該用代碼來說明代碼的行為。代碼本身已經說明了這個問題。相反,文檔應該說明代碼塊 為什麼要做它所做的事。任何讀代碼的人應該早已熟悉用來編寫這些代碼的語言 ― 如果他們不熟悉,則任何語言的文檔也未必能幫得上忙。

但代碼塊是如何與程序其它部分交互的,這一點並不總是很清晰,這就是文檔所要說明的。因為代碼的讀者熟悉(或者應該熟悉)編寫它的語言,所以用與代碼相同的語言來說明代碼背後的意圖就很有效了。

還有,單元測試加速了重構的過程。當一套單元測試可以在任何時間在代碼上運行以確定是否有功能損壞時,程序員可以比在其它任何情況下更有信心重構代碼。所引入的絕大多數錯誤可以被立即檢測出來。

由於這些原因,想要編寫健壯的軟件,單元測試是一個有力的手段。實際上,因為單元測試充當了一種形式的文檔,而且可以自動強制執行(通過將它們包含在構建過程中),所以將測試本身當作系統規范使用的建議聽上去很有誘惑力。

用測試形成規范的 一部分是合理的,因為我們要讓所有有效的實現通過全部系統級別的測試。但是用單元測試形成整個規范有一些嚴重的弊端。

首先,系統的測試集必然是不完整的。無論我們為一個系統指定多少測試,總會有一些系統輸入和狀態是我們所沒有提供的。我們可以將測試解釋為指定它們“最合理”的范圍,但這種范圍是模稜兩可的。

此外,單元測試本質上是強制執行特定實現的特性。系統的實現方法有多種而不僅是一種。因此,將單元測試用作規范和將特定實現用作規范具有許多相同的缺點。

因此,我們最好將單元測試看作是規范的補充,而不是整個規范。

收益可以超過成本

我希望能夠幫您形成這樣的結論,在設計軟件系統時需要擁有盡可能精確的規范,並能夠認識到在形成這個定義時,極端編程模型規范的模塊化構造能夠平衡成本和收益。

還有,我希望自己已經充分闡述了將實現誤作為規范的缺陷,並指出了過分依賴單元測試,希望它完成整個規范定義的僥幸想法所存在的問題。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved