應遵循的PL/SQL編碼規則 - ORACLE (zt)

tolywang發表於2007-07-16
應遵循的PL/SQL編碼規則

作者:Steven Feuerstein

提高編寫PL/SQL程式碼數量及質量的四個簡單易行指導方針

我從1990年就開始編寫PL/SQL程式碼。這意味著我已經編寫了幾萬行的軟體程式碼,但我確信,其中的絕大多數程式碼都非常拙劣,而且難以維護。


幸運地是,我發現找到並遵循編寫出更好程式碼的新方法還為時不晚。就是在去年,我的程式碼質量有了顯著改進;這些改進主要是由於制定了一些簡單的規則,並像紀律一樣加以遵守。

本文為PL/SQL新手及有經驗的開發人員提出了四條建議;遵守其中任何一條,你的程式碼質量都會有提高。這四點建議都採納,你可能會驚奇地猛然發現:你竟然是一個非常好的程式設計師,要遠遠超乎你的想象。

所有工作都獨自完成

我們很少有人是孤立工作的;大多數PL/SQL開發工作是在相對較大的機構中進行的。但我們基本上還是在自己的小隔間裡用自己的裝置獨自工作。幾乎沒有PL/SQL開發小組進行正規的程式碼複查或系統測試。

我不可能透過這篇文章改變你們開發小組的基本狀態。因此,我仔細地選取出以下幾點建議。實施其中任何一點並不需徵得管理人員同意。不論你的小組是大是小,都不必讓其中的每個人都贊同這些編碼規則。你只需按以下建議來改變你的本人的編碼方式:

1. 嚴格遵循命名約定,好像它們就是你的生命支柱。
2. 戒除編寫SQL的嗜好:編寫的SQL越少越好。
3. 使執行部分短小:告別"義大利麵條式的程式碼"。
4. 找一位夥伴:非常贊同找個人來監督你的工作。

1. 遵循命名約定

如果你建立並嚴格遵循一套命名約定,特別是對於應用程式元件的,你就可以節省很多時間。

當然,遵循命名約定的想法並沒有什麼新意,你可能已經聽煩了。所以我並不提出什麼宏偉的命名計劃,而是給出一些非常具體而明確的約定,然後證明這些約定會多麼有用。

前幾個月我一直在為PL/SQL開發人員設計、構建一種新工具。它名為Swyg(可以在中找到),可以幫助程式設計師完成程式碼的生成、測試及重用的工作。它具有幾個獨特的元件。我為每個元件指定了一個由兩個字母組成的縮寫名稱,如下所示:

SF-Swyg的基礎部件
SM-Swyg的後設資料
SG-Swyg的生成程式
SL-Swyg的程式碼庫
ST-Swyg的單元測試

於是,我便遵循表1中的命名約定,同時使用這些縮寫。遵循這些約定有什麼好處呢?一般來講,如果我要求一致的命名規則,我就可以更流暢更高效地編寫程式碼。

明確地說,這些約定具有可預測性,意思是說我編寫的SQL程式能生成有用的指令碼。例如,透過使用表1中的約定,可以生成Swyg中所有基礎包的安裝指令碼。執行這些工作的SQL*Plus指令碼如清單1所示。這類指令碼非常有用,因為它意味著我不必手動維護安裝指令碼。當我向Swyg方案中增加另一個表,並生成一組相關包時,我只要執行我的指令碼,更新後的安裝指令碼便會跳出來。

2. 戒除編寫SQL的嗜好

編寫的SQL越少越好,這似乎與我們的直覺不太一致。對於PL/SQL開發人員來說,這是一個奇特的建議,因為PL/SQL的主要優點之一就是可以毫不費力地在程式碼中編寫SQL語句。不過,這種簡易性也是這種語言的一個致命的弱點。

可以將純粹的SQL語句直接置於PL/SQL程式碼中,而無需JDBC或ODBC之類的中間層。因此,無論何時何地,PL/SQL開發人員只要需要SQL語句,他們通常就會向其應用程式程式碼中嵌入SQL語句。那麼這樣做有什麼問題嗎?

在PL/SQL程式碼中到處使用SQL語句必然會導致以下後果:

 

儘管實際表現不同,但同一邏輯語句仍會出現重複,從而導致過多的語法分析,且難於最佳化應用程式的效能。
暴露商務規則和方案。這直接在SQL語句中包含了執行商務規則的邏輯。這些規則總在變化,所以應用程式的維護成本會急劇增加。
 

當然,你要編寫的每一個PL/SQL應用程式幾乎都是基於基礎表和檢視的。你需要執行SQL語句。問題不在於是否執行,而是何時執行、如何執行。

如果你對資料結構進行封裝,或者將它們隱藏於一個PL/SQL程式碼層(通常是一個程式碼包)之後,那麼你的應用程式將會更健壯,而且你還會發現建立和維護變得更易多了。

我們來看一個簡單的例子。 假定我需要編寫一個處理某員工工作的程式。第一件事是獲取該員工的全名,定義為"姓名逗號(,)姓";然後我可以進行詳細分析。清單2給出了這種情況下我很可能要編寫的這類程式碼的一個示例。

一切似乎都是這麼簡單和直接;這些程式碼可能會有什麼錯誤呢?實際上真是非常糟糕。最主要的是我暴露了一個商務規則:全名的結構。我可能要花費數小時來對此程式碼及其所基於的應用程式進行測試。但就在它剛剛投入使用時,我才知道客戶會不斷地打電話告訴我,實際上,他們的全名應該表示為"名空格姓"。

現在怎麼辦?搜尋所有位於引號內的單個逗號?

現實的解決方案是使用隱藏所有細節、只提供一組預定義、預測試及預最佳化並能完成所有任務的程式包。清單3為基於封裝程式碼重新編寫的process_employee過程。hr_employee_tp包提供了用於定義儲存姓名的區域性變數的型別;hr_employee_rp包含有基於一種商務規則而返回全名的函式。

將顯示PL/SQL語句灌入SQL程式碼很容易,同樣,談論封裝這些語句是如何重要也不費勁。但另一方面,編寫執行封裝任務的程式碼卻具有挑戰性;甚至是不現實的。生成這些包或許更有意義。

幾年前,我曾幫助構建這樣一個生成程式。該程式段為PL/Generator,現在由Quest Software公司擁有,PL/SQL開發社群可以免費使用。你可以從我的網站下載。要知道,其封裝體系結構與我在前面所概括的約定不同。PL/Generator建立了一個單獨的包,它包含了一個表的型別、查詢和變化邏輯的全部內容。

當你不再編寫太多的SQL,而是呼叫執行SQL的程式時,無論你是生成還是編寫自己的定製封裝,你的應用程式都會受益匪淺。

3. 使執行部分短小

面對現實吧:總是與我們的判斷和最新的一系列新年決議相左,我們必須停止編寫義大利麵條式的程式碼:龐大而冗長,人們實際上不可能理解它們,更不用說維護或升級了。怎樣才能避免"義大利麵條"呢?

實際上,答案很簡單:決不允許執行部分超過50或60行。這種大小使你能在一頁紙或一個螢幕上檢視該程式碼塊的整個邏輯結構,這也意味著你可以真實地領會該程式的意圖,而且完全憑直覺就能理解它。

你可能非常同意上述觀點,但同時又嘲笑我的建議:程式程式碼永遠不超過50行。沒錯,你應當嘲笑,因為這當然是不可能的。毫無疑問,你需要超過50行的可執行程式碼;問題是你把這些程式碼放在哪,以及你怎樣加以組織。

如果採取以下做法,你的確能夠應對各種複雜的要求,並把程式碼限制在50行以內:

將所有的商務規則和離散邏輯塊置於其自已的程式(通常是函式)中,從而在任何可能的時候慎重地重用程式碼。
儘量使用在程式的宣告部分定義的區域性模組、過程和函式。
假定我在編制一個呼叫中心應用程式。我需要編寫一個程式,它要滿足下面的要求:

"對於特定部門的每個員工,將其工作量(分派給該員工的呼叫次數)同該部門員工的平均工作量進行比較。如果某員工的工作量低於平均工作量,便將下一待處理呼叫分派給此人,並基於這種情況安排約定。"

我從以前的工作中獲悉:我的朋友Claudia已經編寫了一個分析包,它會返回工作量方面的資訊。但是分派待處理呼叫和安排約定都是全新的工作,需求文件的其餘部分對此進行了詳細說明。

最初我想把這15頁的內容全都看完,但我沒有那樣做。我使用了一種稱為"逐步求精法"或"由頂向下設計"的技術,並先編寫了清單4中的程式碼來實現該程式。

下面給出了清單4中最關鍵程式碼行的解釋;由該程式(緊湊的執行部分)的最後開始,向上進行。這似乎有悖於直覺,但這的確是通讀用逐步求精法編寫的程式的最好方式。

第22~30行。用一個遊標FOR迴圈(cursor FOR loop)來對指定部門的所有員工進行迭代處理。在第24~25行,利用分析包中的程式判定當前的員工是否工作量不足。在第27~28行,呼叫三個程式:assign_next_open_case、schedule_case和next_appointment。我還不知道怎樣實現這些程式,但我知道它們透過其名稱和參數列表達了需要事先完成的工作。

第10~19行。為第27~28行中的三個程式建立"stub",也就是佔位程式。注意,它們是區域性模組,在assign_workload中進行定義,且不能從其他任何程式呼叫。

第5~8行。定義一個遊標,以獲得指定部門的所有員工。現在可以設法編譯此程式碼。

對這樣一個小程式成功完成編譯好像是個小勝利,也的確如此。完成正確編譯,然後是簡單測試,然後增加一點程式碼,再進行正確編譯,以此類推,諸如此類的小勝利締造出構造精良的程式,而且會非常滿意。

我還可以驗證該分析程式是有效的,並且找出了要分派的任務適當僱員。這些工作全部完成後,我將從三個程式中挑出一個,比如assign_next_open_case,進行下一步或下一級別的精細設計。我要閱讀該任務的文件,並在assign_next_open_case裡編寫一個簡短的執行部分,它可反映該任務的概況。

很快,我的區域性過程有了它自己的區域性過程和函式,但在該過程的每一步,我的程式碼都很短、可讀、易於測試、可根據需要進行調整。

4. 找一位好夥伴

計算機並不會程式設計,人才會。

有多少次你彎著腰、駝著背坐在計算機前,因無法找出程式碼中的錯誤而感到非常鬱悶?先是幾分鐘過去了,接著又過了幾小時。最後,對自己都厭煩了,感到非常失敗,你把頭伸出你的小隔間並請朋友過來幫你看一看。

通常會有下面三種情況之一出現:

當你的朋友從她的椅子上站起來時,一切都在瞬間變得非常清楚。
你的朋友瞥了一眼螢幕,馬上就指出了問題所在。
你的朋友不負責該系統中你所做的部分,所以你必須說明你的程式在幹什麼。當你逐步講解邏輯時,引起錯誤的問題所在會突然暴露在你面前。
事實就是自己很難除錯自己的程式碼,因為你自己對它太投入、太專注了。


這個問題最好的解決辦法是由開發經理創造這樣一種文化:各種想法是共享的、不懂是可以原諒的並不會受到處罰、定期進行建設性的程式碼評審。不幸的是,這些文化上的改變是難以實現的。

與此同時,我建議在幫助改變你所在小組的文化的過程中你應起帶頭作用。找到另一位開發人員,最好比你經驗豐富,並建立一種"夥伴"關係:在出現問題時,他可以充當你的參謀,當然,你也可以充當他的參謀。事前達成共識:不知道所有問題的答案並沒有什麼不對。

然後為你自己制定一條簡單的規則:不要為一個錯誤苦思冥想超過半個小時。30分鐘過去後,把你的夥伴叫過來,讓人類心理學為你服務,而不是跟你作對。

獲得一種新工作方式的四個步驟

本文為你提供了可以採取的用於改變你的程式設計體驗四個步驟,而無須投資新的工具或改變整個小組的工作流程。這四步甚至可以不全部遵循,只要遵守一步都會讓你受益。

Steven Feuerstein () 是PL/SQL語言方面的一位權威人士。Feuerstein撰寫了9本有關PL/SQL (全部由O'Reilly & Associates出版公司出版)的書籍,其中包括《Oracle PL/SQL最佳實踐(Oracle PL/SQL Best Practices)》和《Oracle PL/SQL程式設計(Oracle PL/SQL Programming)》。他還是Quest Software公司的資深技術顧問。

物件名稱結構註釋生成(DDL)檔案
_表名,如SM_TASK,用於在普通的Mentat元件中定義的任務。_.tab
主鍵列id標準是:(幾乎)每個表都包含一個按一個序列生成的id列。對於交叉表等有一些例外。N/A
用於生成主鍵的序列__seq用於一個表主鍵的序列名,如SG_SCRIPT_SEQ,它為Mentat產生指令碼表生成一個新主鍵。_.seq
查詢封裝包__qp包含用以在基礎表(如SL_SOURCE_QP)中檢索資料的標準API,它有助於在Mentat可重用庫中查詢各元素的原始碼。說明:__qp.pks
主體:__qp.pkb
改變封裝包__cp包含用以改變(INSERT、UPDATE、DELETE)某基礎表(如ST_UNITTEST_CP)中的資料的標準API,它使我能夠維護單元測試定義。說明:__cp.pkb
主體:__cp.pks
型別封裝包__tp包含用於指定表(如SG_GEN_RUN_TP)的預定義型別--包括集合、REF CURSORS、記錄。說明:__tp.pks
主體:NA (type definitions do not need a package body)
規則封裝包__rp包含程式,通常是函式,它隱藏了關係到該實體的商務規則的細節。說明:__rp.pks
主體:__rp.pkb
其他物件包__xp包含特定實體的自定義邏輯。說明:__xp.pks
主體:__xp.pkb

 

關鍵字
  • 是由兩個字母組成的縮寫。對於Swyg,它可以是SF、SM、SG、SL或ST。
  • 是商務實體名,如TASK用於定義某人要完成任務列表;SCRIPT用於程式碼生成模板;等等。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/35489/viewspace-84789/,如需轉載,請註明出處,否則將追究法律責任。

相關文章