编程风格是一个经久不衰的话题,大家所公认的事实是:一个良好的编程风格会带来很多的好处。而对于“良好”的标准,则众说纷纭,莫衷一是。编程风格在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支持的单行最大长度——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