如何減少ABAP業務代碼的複雜度

在程序開發的過程當中,相同的功能每每有不一樣的實現方式。對於能夠實現一樣功能的不一樣代碼,複雜度是用於比較其質量優劣的重要指標。html

在本文中,代碼複雜度是指代碼被理解/修改的難易程度。越容易被理解、修改的代碼的複雜度越低;反之其複雜度越高。程序員

複雜度低的代碼比複雜度高的代碼有更多好處,好比,面試

  • 從代碼「查邏輯」變得簡單
  • 能夠節省修改的時間
  • 下降在將來引入bug的概率
  • 新人會更容易上手現有代碼
  • 幫助整個系統更加「長壽」

ABAP開發是在SAP系統中進行的,而SAP是企業的核心信息系統,其中會包含複雜的業務邏輯,一般由ABAP實現,並須要長期的維護。在這樣的工做中,ABAP代碼的複雜度對系統維護成本甚至項目的成敗有着重要的影響。數據庫

在下文中,我會介紹幾種有助於最小化代碼複雜度的通用思路,並嘗試把它們和實際的ABAP開發工做結合起來,幫助理解。編程

做者水平有限,若是讀者發現了任何問題,歡迎評論指出。app

 

本文連接:http://www.javashuo.com/article/p-vzshfkof-co.html編輯器

原創內容,轉載請註明ide

1, 模塊化設計

兩年前,我第一次參與的SAP項目剛完成不久。那時我對本身的技術至關自信,在項目中,我不只完成了多種類型的功能開發,並且也讀完了整個項目的新開發代碼。即便對於一些沒作過的新的功能需求,我也每每能在不依賴乙方同事的狀況下獨立查找資料完成。自信滿滿的我決定換份新工做,並獲得了一個面試機會。第一輪面試考察的是一些經常使用功能的實現和對業務流程的瞭解程度,如我預想的那樣,本身順利經過。在第二輪面試中,對方問到"對模塊化的理解和實踐",這是個出人意料的問題,我努力地思考了一番,殊不知道該說什麼,因而被客客氣氣請了出去…模塊化

經歷了此次失敗後的我,不斷地思考着模塊化設計。若是再次面對那個問題,也許我應該這樣回答:函數式編程

定義

模塊是系統中獨立的、可替代的單元。模塊化設計,便是把系統分解爲模塊的集合。模塊的形式多種多樣,能夠是form、method、function Module、class、或report等。在理想的世界中,每一個模塊都徹底獨立於其它模塊:開發者在任何模塊中工做的時候,都不須要知道有關其它模塊的知識。在這種理想狀態下,系統複雜度取決於系統中複雜度最高的模塊。

固然,實踐與理想不一樣,系統模塊間總會多少有些依賴。當一個模塊變化時,其它模塊可能也須要隨之而改變。模塊化設計的目標就是最小化模塊間的依賴。

爲了管理依賴,咱們能夠把模塊當作兩部分:接口實現

接口包含了所有的在調用該模塊時須要的信息。接口只描述模塊作什麼,但不會包含怎麼作

完成接口所作出的承諾的代碼被稱爲實現

接口中包含2種信息:正式的和非正式的。

正式的信息在代碼中被顯式指定,程序語言能夠檢查其中的部分正確性。好比,方法的簽名就是正式的信息,它包含參數的名稱和類型,返回值的類型,異常的信息。不少程序語言能夠保證代碼中對方法的調用提供了與方法定義相匹配的參數值。

接口裏面也包含非正式的元素。非正式部分沒法被程序語言理解或強制執行。接口的非正式部分包含一些高層行爲,好比函數會根據某個參數的內容刪除具備相應名字的文件。若是某個類的使用存在某種限制,好比方法的調用須要符合特定順序,那這也屬於接口的一部分。凡是開發者在使用模塊時須要瞭解的信息,均可以算做模塊接口的一部分。接口的非正式信息只能經過註釋等方式描述,程序語言沒法確保描述是完整而準確的。大部分接口的非正式信息都比正式信息要更多、更復雜。

正式的信息和非正式的信息都是複雜度的來源,清晰的接口定義有助於開發者瞭解在使用模塊時須要知道的信息,從而避免一些問題。

示例

以function module爲例,在function module編輯器中看到的function module名,和前6個標籤,加上function module文檔(若是有),都屬於它的接口。而source code中的代碼,則屬於實現。以下圖

 

固然,若是該function包含任何隱性的使用信息,它也算作接口的一部分。好比,若是使用SAVE_TEXT保存長文本,一般要有一個COMMIT操做來提交修改。「須要使用COMMIT提交修改」,一樣是SAVE_TEXT的接口的一部分。

進行模塊化設計,是減少代碼複雜度的第一步。由於,開發者在進行模塊內部開發時,只須要關注 當前模塊的接口+當前模塊的實現+其它相關模塊的接口。他只須要關注整個軟件系統的一小部分,接觸的東西變少,會使理解工做內容的速度大大增長,也會使犯錯的機會變小。對於試圖理解當前系統的部分功能而不作修改的人,這種設計一樣會減輕人們的負擔,由於人們一般只須要經過模塊們的接口來了解程序的功能。

2, 減小異常的影響

經驗較淺的程序員容易犯的一個錯誤是,只考慮程序中的正常狀況,即所謂的happy path,而沒有(足夠多地)考慮異常情形。不周全的考慮可讓程序員快速完成功能,可是接下來則會致使測試中的頻繁翻車,程序員不得再也不對程序進行種種的修補,致使代碼總體的複雜度迅速升高。此外,即使是在一開始已經考慮到了各類異常情形,爲了處理它們,也會給程序增長必定的複雜度。本節內容的主題是,如何合適地儘可能減少由異常情形引發的複雜度。下面介紹具體的三種辦法,

從概念上消除異常

第一種辦法是從概念上消除異常。異常是相對正常而言的,功能的定義能夠影響到異常的定義。ABAP SQL有插入語句,代碼以下,

INSERT ztable FROM TABLE @lt_something.

可是,開發者一般不得不考慮主鍵重複引發的異常處理。因此在這一語句後面,還要檢查sy-subrc返回值,根據判斷作進一步處理...代碼所以變得複雜。

如何避免此處的異常處理?假設咱們對功能的設定作出一些改變,從「把內表的數據插入數據庫」改成「保證數據庫中存在內表的數據」,在這個新功能的內部判斷插入語句的執行狀況,若是由於主鍵重複致使插入失敗,則改成按主鍵更新數據庫表,此時再也不須要在調用時進行相關異常處理。

說到這裏讀者已經知道,這個「新功能」就是ABAP中的關鍵字MODIFY,

MODIFY ztable FROM TABLE @lt_something.

使用MODIFY而不是INSERT的話,一種常見異常的定義便消失了,代碼的總量和複雜度所以會減小。固然,前提是MODIFY的功能和需求相匹配。有些資深開發者由於懼怕新人不瞭解MODIFY的原理而禁止他們使用這個關鍵字,是因噎廢食的作法。

隱藏異常

把異常隱藏在較低層面是第二種作法,這種作法可使高層的代碼在不須要了解異常存在的狀況下進行工做,從而減小高層的複雜度。SAP系統中的一個例子是tRFC

對於tRFC而言,遠端系統不須要在RFC客戶端程序運行tRFC的時候可用。tRFC組件將被調用的RFC函數和相關數據存儲在SAP系統的數據庫裏,包含一個惟一的事務標識符(transaction identifier,TID)。若是調用發送了,接收系統倒是宕機狀態,調用會保留在本地隊列中一段時間。調用對話程序能夠在不等待遠程調用成功/失敗的狀況下繼續運行。若是接收系統在一段時間後仍然不可用,調用將被計劃爲後臺做業運行。

在tRFC的例子中,高層調用者不須要了解對方系統的狀態,也不須要進行傳輸失敗的處理,這一切都由低層完成了。高層程序的複雜度也會所以獲得控制。

聚合異常

與其分散地處理程序中不一樣部分產生的異常,不如把它們集中交給高層的程序,進行統一的處理。SAP系統中的一個例子是BAPI。絕大多數BAPI使用一個名爲RETURN參數返回全部的錯誤,這樣一來就能夠由高層調用者對可能產生的錯誤進行統一的處理,而不是在產生錯誤的地方進行分散、個別的處理。

3, 純函數

 

「Hi 氫氦,我在測試中遇到了這個錯誤消息,麻煩你看一下緣由。」

「什麼?這和咱們的修改毫無關係,這個報錯屬於B功能,而你知道咱們改的只是程序的A功能,!」

「是的,可是我得保證此次修改沒有影響到B功能,因此請你調試調查緣由。」

測試的一個難題是測試者不知道看似單純的修改會帶來什麼樣的複雜問題,因而只好求助於人工檢查,開發者每每就是那個不幸的工人,使用純函數能夠幫助避免這類狀況的發生。

定義

最近流行的函數式編程十分強調純函數的概念。純函數是指符合如下條件的函數,

  • 對於相同的輸入,函數總有相同的輸出。

這要求函數內部不能存在「反作用」。

它的輸出結果的肯定不該該依賴輸入參數外的任何內容,例如,不能夠由於本地測試環境中沒有相應的數據庫就產生「鏈接數據庫異常」致使沒法返回結果。

它也不該該改變除了返回結果之外的任何內容,例如,不能夠改變全局可變狀態。

知足以上條件的函數,能夠被稱爲純函數。

從模塊化的角度來看,全局狀態和對外部系統的鏈接都屬於接口的一部分。純函數不會與這些東西產生交互,所以它的接口會更簡單,複雜度更低

雖然ABAP不是函數式語言,但它依然能夠有純函數,而且開發者能夠經過寫純函數而受益。

在上面的例子中,若是開發者能夠證實A、B功能分屬2個模塊,並且它們都屬於純函數,那麼只要證實A的變動不會改變B的輸入,便可證實修改沒有致使對方給出的錯誤。

4, 需求文檔與實現

系統中的模塊能夠分爲接口和實現,換一個角度思考的話,代碼也能夠看做需求文檔的實現。需求語言的準確性,對代碼產物的質量有着直接的影響。在SAP開發中,業務邏輯的實現是首要目標,業務複雜度也每每是代碼複雜度的最主要來源。

信息丟失

程序是需求的實現,所以代碼應當儘可能包含需求文檔中的信息。在需求文檔的質量可靠的前提下,這樣作能夠有效提升程序的可讀性,從而下降其複雜度。信息丟失的一個極端例子是代碼混淆,顯然混淆後的代碼複雜度將大大升高。

固然,程序語言和系統內部的信息和需求文檔中的天然語言是有差異的,這也是程序員存在的意義。在實際的開發中,能夠嘗試經過增長一個抽象層的方式來保留需求文檔中的信息。好比,在高層對需求語言進行建模,在低層實現實際的執行過程。

注意:若是需求文檔很長,代碼實現不多的話,意味着工做可能存在某些問題,多是需求內容冗餘、功能設計不合理、實現不完整、信息丟失等,此時要從新思考相關工做內容,以確保未出現這些問題。

詞彙表

保留信息的前提條件是明確信息,需求文檔中應當有詞彙表,幫助開發人員迅速捕捉和理解最重要的業務語言,以便把它們落實在程序中。

對於在中文環境下的工做而言,這點尤爲重要。ABAP並不適合使用中文命名,而普通的程序員沒有能力把中文的業務語言轉換成英文的,須要由業務顧問來完成這項意義重大的工做。

5,工做轉移

最後,還有一種顯而易見的辦法是將某些業務邏輯轉移到ABAP以外實現,好比下推到數據庫層面、使用配置工具實現(BRF+)、交給中間件等。

優勢:從ABAP角度來看,這種辦法最有效地避免了複雜度的增長。

缺點:從全局來看,複雜度並無真正消失,只是隨着工做量轉移到了另外一個地方。

要從系統的總體複雜度的角度來考慮實現邏輯的位置,好比,人們經常使用配置表配合ABAP代碼來實現自定義邏輯,BRF+中的decision table能夠實現類似的功能,若是需求但願獲得用戶在實際使用程序時對不一樣邏輯的命中率的話,那麼decision table可能會更合適一些,由於BRF+中包含跟蹤模式,能夠被直接利用,這樣就能夠經過寫簡單的代碼來獲得結果,避免引入更多的複雜度。

後記

長期關注本博客的讀者可能會注意到,這個博客已經有段時間沒有更新ABAP相關內容,這是由於從今年開始,個人工做重心已經逐漸轉移到Spark和Dynamics等其它方面的開發,花在SAP上面的時間變得不多。剛好最近參加了一個新的SAP項目,它喚醒了我對ABAP的一些記憶,因而趁熱打鐵,寫了這篇文章。這篇文章算是在ABAP角度上將最近學習到的東西進行的一個總結複習。但願它能對讀者有所幫助,也真誠地但願能收到反饋,共同討論相關話題。

 

參考連接:《A Philosophy of Software Design》

        《函數響應式領域建模》

        軟件設計之Deep Module(深模塊)

     個人Spark SQL單元測試實踐

相關文章
相關標籤/搜索