Fork me on GitHub

写具有良好风格的ABAP代码

编程风格是一个经久不衰的话题,大家所公认的事实是:一个良好的编程风格会带来很多的好处。而对于“良好”的标准,则众说纷纭,莫衷一是。编程风格在ABAP程序中当然也有着重要的意义,因为很少看到专门针对ABAP编程风格的讨论,我决定把我知道的事情总结出来,以抛砖引玉。欢迎看到这篇文章的朋友回复讨论。

 

2020.06.16更新:SAP推出了一份很好的styleguides,是一份很好的指导文档,对代码风格方面的问题做出了详细的解释,纠正了业界流行的很多错误实践,强烈推荐。

2018.01.11更新:本文中提到的大部分内容可以通过Code Inspector进行检查,Github上面的一个开源检查项目abapOpenChecks提供了相当全面的检查,强烈建议读者使用。相关文档地址:http://docs.abapopenchecks.org/checks/

 

本文链接:http://www.cnblogs.com/hhelibeb/p/6814045.html 

原创内容,转载请注明

1,大写与小写

如我们所知,ABAP是一种大小写不敏感的语言。这自然会引起一个问题:使用大写还是小写?SAP给出的ABAP编辑器为我们提供了4种选项:

  • (全部)大写
  • (全部)小写
  • (关键字)大写
  • (关键字)小写

选择(关键字)大写,让代码的其余部分保持小写,在我看来是一个极为自然的选择。理由是,一)阅读大写字母组成的文本比阅读小写字母组成的文本要难。二)程序的读者通常会对关键字极为熟悉(即使不熟悉,也有文档可看),而作者本人之外的读者,对作者写出的非关键字不太可能熟悉。这两个理由使得,相比关键字,我们更需要让代码的非关键字保持良好的可读性。因此,非关键字的小写是一种必然的选择。在此基础上,让关键字保持大写,可以帮助我们区分关键字和非关键字。当然,由于关键字高亮的功能的存在,也可以不通过大小写区别它们,所以(全部)小写同样是一种可行的选项,部分SAP标准代码也是这样的风格。

某些开发者会告诉新人:代码最好全部大写,这样可以避免自己不小些将大写参数设定为小写(ABAP中很多字符参数是大写的)。然而,由于上述的原因,这是一件很糟糕的事情。字符的值的大小写和代码的大小写完全是两码事,因为这种理由放弃代码的可读性,是不成立的。

2,缩进

据说很多程序员常年为了缩进的问题争论不休,ABAP开发者在这方面是幸福的,因为SE38的代码编辑器提供了自动缩进的功能,这使得只要点击“格式优化”,所有人的代码会得到同样的缩进...

我还没有使用过新的编辑器——ABAP Development Tools for Eclipse。也许在这个新的IDE普及之后,人们会对ABAP的缩进产生新的看法。

3,表达式vs关键字

ABAP是一门包含有大量关键字的语言。SAP似乎意识到了关键字过多带来的不便,在尝试着在近期的更新中引入更多表达式的写法。

表达式的写法比关键字更加简洁、可读,推荐尽量使用表达式代替关键字,比如:

"实例化对象

 DATA(e_receiver) = NEW event_receiver( )."推荐的写法

 DATA e_receiver TYPE REF TO event_receiver. "不推荐的写法
 CREATE OBJECT e_receiver.

 

*调用方法(可以看到,传统的写法居然要5行...
val = object->method( parameter = a ) "建议的写法

CALL METHOD object->method  "不建议的写法
    EXPORTING 
       parameter = a 
    RECIEVING
       return    = val.
*访问内表
SELECT * INTO TABLE @DATA(itab) FROM sflight
  UP TO 10 ROWS ORDER BY carrid.

TRY.
    DATA(ls_sflight) = itab[ 2 ].   "推荐的写法
  CATCH cx_sy_itab_line_not_found.

ENDTRY.

DATA(ls_sflight) = value #( itab[ 2 ] optional ). "更推荐的写法 ,value表达式可以自动捕捉异常
DATA ls_sflight TYPE sflight. READ itab INTO ls INDEX 2. "不推荐的写法 IF sy-subrc <> 0. ENDIF.

 有关更多表达式写法的例子可以参考这个博客:ABAP 7.4新特性,或者ABAP Objects,以及SAP的官方文档。

4,Open SQL

 如果要从一个数据库表中取得它自身的两个字段比较后的到的条目,ABAP的新手可能会这样写:

SELECT carrid connid fldate seatsocc seatsmax
       FROM sflight
       INTO TABLE sflight_tab
       WHERE seatsmax < sflight-seatsocc.

而有经验的/看过文档的人知道上面的代码会报错,正确的写法是这样:

SELECT carrid connid fldate seatsocc seatsmax
       FROM sflight
       INTO TABLE sflight_tab
       WHERE seatsmax < sflight~seatsocc.

区别就在与-和~。

 

不过第一种写法在某些情况下也是可以运行的,如果程序中有这样的声明的话:

DATA:
  BEGIN OF sflight,
    carrid   TYPE sflight-carrid,
    connid   TYPE sflight-connid,
    fldate   TYPE sflight-fldate,
    seatsocc TYPE sflight-seatsocc,
    seatsmax TYPE sflight-seatsmax,
  END OF sflight,
  sflight_tab LIKE STANDARD TABLE OF sflight WITH EMPTY KEY.

此时程序也可以运行,不过比较不会发生在数据库字段之间,而是会以本地定义的结构sflight中的seatsocc作为条件。

 

要避免这类混淆的发生,可以使用转义字符@,加在SQL语句中的Host Variables前面。

如果是想比较数据库内的字段的话:

SELECT carrid, connid, fldate, seatsocc, seatsmax
       FROM sflight
       WHERE seatsmax < sflight~seatsocc
       INTO TABLE @sflight_tab.

而如果是想要以ABAP程序内的值作为条件的话,就要在它的前面也加上@:

SELECT carrid, connid, fldate, seatsocc, seatsmax
       FROM sflight
       WHERE seatsmax < @sflight-seatsocc
       INTO TABLE @sflight_tab.

这样就不会混淆了~

(注:本节的内容来自一篇英文博客:Why the new Open SQL Syntax is Better

此外,在S4/HANA中,SAP推荐将更多的计算内容放到数据库中。为了实现这一目的,现在Open SQL具有CASE表达式、字符串表达式、CAST、CTE等多种新功能。我们应该尝试使用它们。

5,命名法

 ABAP程序通常使用一系列前缀来为变量命名,比如:

LT_ = Local internal table

LS_ = Local structure(work area)

LR_ = Local reference

GT_ = Global internal table

GS_ = Global structure(work_area)

GR_ = Global reference

这样做是有好处的,一方面,通常的ABAP编辑器不具备自动提示类型的功能,合理前缀可以降低阅读代码的心智负担;另一方面,如上一节所述,如果为变量取一个和数据类型/数据库字段完全相同的名字,会在某些情况下产生意外的混淆。比如:

DATA s1 LIKE sflight.
DATA s2 TYPE sflight.
"以上这段代码会声明两个相同的结构s1, s2


DATA sflight TYPE i.

DATA s1 LIKE sflight.
DATA s2 TYPE sflight.
"如果声明过一个名为sflight的i类型变量,则使用like的语句会声明一个i类型的s1,使用type的语句会声明一个有着sflight行类型的结构s2..

 

但是前缀的滥用也会导致很多问题,合理的ABAP代码中应该尽量避免多余的变量名前缀。

比如

  • l_carrid(较好)
  • lv_carrid(较差)

使用lv/gv的前缀来表示本地变量/全局变量,是一个比较不明智的做法。因为,一个变量是值这种事情通常是无需说明的。v可以被当作默认值省略,而lt/lc则有意义。

同样的,为form命名时也不应当存在这种无意义的前缀:

  • get_price(正确)
  • frm_get_price(错误)

因为通常来说,form的使用是通过PERFORM关键字来实现的:

PERFORM get_price.

这时,get_price显然不可能是form之外的任何存在。使用frm_这样的前缀,不能带来任何理解上的帮助,只会增加代码阅读的难度。

2019.07.23更新:

QQ群里有群友质疑:lv_是通行的命名前缀,l_会带来混淆,让开发者感到困惑。

在这里回应下,首先,这种命名方式并不是笔者的发明,

sap的很多代码中的变量会用l_或m_作为前缀,比如我们最常用的cl_gui_alv_grid中的一些成员变量的前缀就是m_。笔者只是认同这种办法的合理性。

在命名中,前缀后面的东西比前缀重要很多,所以要尽可能降低前缀的复杂性,避免浪费读写代码的时间。使用l_而非lv_可能会稍微增加命名上的复杂性,但是如果开发者可以在一开始掌握一个稍微复杂点的规则,也许可以在后面省更多力气。

SAP的最新建议不要使用前缀,这也证明了前缀的重要性远不及变量名的主体的重要性。

6,单行长度

 有种观点认为,单行的代码长度不应超过80个字符。大体上,对于ABAP代码而言,我同意这个观点。

80个字符在ABAP编辑器中的视觉效果

 

如图,80个字符已经稍稍超出了编辑器核心区域的边界(虽然远未达到ABAP支持的单行最大长度——255字符)。如果只是打开单个编辑器窗口的话,这种长度还可以接受,但如果要并排打开2个窗口,一部分代码也许会无法直接显示。

此外,在SAP自身的代码比较工具中,过长的单行内容是无法直接展示的:

过长的单行代码不能一次展示

这种情况下,需要点击工具栏中的按钮换页:,非常不利于阅读。如果能有意限制单行代码的长度,就可以避免处于这种不利的情况。

7,带表头的内表

也许每个ABAP初学者学习的第一样东西都是内表,而学习内表时要学会的首要事项就是工作区、带表头的内表与不带表头的内表的区别....会有教程告诉他们用OCCURS关键字声明一个带表头的内表,

DATA: BEGIN OF lt_numbers OCCURS 0,
        num1 TYPE i,
        num2 TYPE i,
      END OF lt_numbers.

或者这样,使用WITH HEADER LINE,

DATA: lt_sflight TYPE STANDARD TABLE OF sflight WITH HEADER LINE.

如果这样做的话,声明得到的内表就会代表两样东西,比如,lt_sflight实际上代表着作为表头的结构lt_sflight、和内表本身lt_sflight[],至于在具体的代码中它到底代表哪个,只能由语境和开发者的意图决定...

这种奇怪的特性似乎是为了开发人员的方便而设计的,但在生产实践上,它使得开发者极易不慎将一个名称代表的两个实体混用,从而写出有bug的代码。SAP在意识到自己的错误之后,把这两种声明方式标记为过时的语法,并且在OO模式下,会在语法检查中提示这一点。

然而,为了兼容旧有的程序,在report和function group等类型的程序中,人们依然可以使用这两种方式声明带表头的内表。很多古老的ABAP教程也大量地使用了它们。以至于不少新人无意识地把它们当作声明内表的合理方式。听说某些公司在规范中禁止了它们的使用,在2017年的现在,我认为这是一项非常合理的举措,用同一个名字代表两样不同的东西本来就是很不好的事情。为了让书写者不至于混淆、为了让读者更好的理解代码,请放弃带表头的内表。与之类似的tables关键字,也应避免使用。

 

 

参考: ABAP Programming Guidelines

    BEST PRACTICE GUIDELINES FOR DEVELOPMENT – USEFUL TIPS FOR ABAP DEVELOPMENT

   

 
 
 
posted @ 2017-05-06 15:11  氢氦  阅读(6154)  评论(10编辑  收藏  举报