1.概述
何为模块化,以及模块化的优点不多赘述,大家都懂。在ABAP中,有多种模块化技术,包括前面提到的处理块,这是最基本的ABAP模块,每一个处理块的代码部分形成一个整体。具体来说,事件块与特定事件和用户事件相关,由事件触发,不需要特定语句进行调用。而对话模块和过程则需要在其他处理块中被调用。
2.子程序
通常情况下为了使程序代码结构显得较为清晰,子程序的定义部分往往放在程序的最后。
1.子程序定义
定义基本语法如下
FROM subr [USING ... [VALUE()Pi[]] [TYPE t | LIKE f] ... ]
[CHANGING ... [VALUE()Pn[]] [TYPE t | LIKE f] ... ].
...
<subr codes>
...
ENDFORM.
一个FORM语句必须有一个对应的ENDFORM语句,而且子程序不能进行嵌套(所有的处理块均不能嵌套)。USING和CHANGING(以及不常用的TABLES选项)是子程序与主程序的参数接口部分,<subr codes>是子程序的具体代码,主程序就是通过调用子程序名subr来执行的。
在主程序中调用子程序的语法如下
PERFORM subr [USING ... Pi ... ].
[CHANGING ... Pn ...].
REPORT z_subroutine_defination_call.
DATA: int1 TYPE i VALUE 1,
int2 TYPE i VALUE 2,
sum TYPE i.
START-OF-SELECTION.
PERFORM add.
WRITE sum.
FORM add.
sum = int1 + int2.
ENDFORM.
2.参数接口
形参与实参不多赘述,在ABAP程序中,使用USING和CHANGING进行参数的传递。
REPORT z_subroutine_defination_call.
DATA: int1 TYPE i VALUE 1,
int2 TYPE i VALUE 2,
int3 TYPE i VALUE 3,
int4 TYPE i VALUE 4,
sum TYPE i.
START-OF-SELECTION.
PERFORM add USING int1 int2.
PERFORM add USING int3 int4.
FORM add USING p1 p2.
sum = p1 + p2.
WRITE: / 'int1 + int2 =', sum.
ENDFORM.
3.参数传递方式
形参定义时的不同类型将影响参数传递的具体过程。子程序的参数传递大体分为值传递(passing by value)和引用传递(passing by reference),以及一种特殊的值和结果的传递。
- 引用传递指的是在子程序调用期间,将实参的地址传递给形参,而形参本身不占用内存。即子程序对形参的进行更改,主程序中的数据值也将更改。定义引用传递形参时在USING和CHANGING后面的列表中指定形参和实参,不需要VALUE选项。通过引用传递进行值传递时,FORM语句中的USING和CHANGING这两个选项并没有什么区别。
USING | USING VALUE | CHANGING | CHANGING VALUE | |
---|---|---|---|---|
参数传递方式 | 引用传递 | 值改变 | 引用传递 | 值改变 |
值改变否 | 改变 | 不改变 | 改变 | 当正常结束后改变(ENDFORM,CHECK和EXIT) |
- 值传递时形参时作为形参的副本创建,形参有自己的内存,更改并不影响实参本身。值传递时应该使用USING...VALUE选项。值传递中的VALUE关键字只是在定义时用,使用PERFORM语句调用子程序时无需出现。
- 值和结果传递,语法如下
FORM ... CHANGING ... VALUE(Pi1) ... VALUE(Pi2)
。该传递中的值在子程序运行期间类似值传递,当子程序结束后,把修改后的值赋值给实参,类似引用传递。然后一旦程序非正常中断,参数变量仍然保持原址。
所谓非常结束主要指因为错误消息或运行时错误等导致的退出。无论那种传递方式,在PERFORM语句中的USING和CHANGING选项,是不存在任何差别的。
REPORT z_subroutine_defination_call.
DATA: int1 TYPE i VALUE 1,
int2 TYPE i VALUE 2.
WRITE:/ 'Before Calling Subroutine :',
int1, int2.
PERFORM self_add USING int1 int2.
WRITE:/ 'After Calling Subroutine :',
int1, int2.
FORM self_add USING p1 TYPE i VALUE(p2) TYPE i.
p1 = p1 + 1.
p2 = p2 + 1.
WRITE:/ 'Inside Subroutine :',
int1, int2.
ENDFORM.
4.参数类型及兼容
在子程序接口定义一个形参可以代表多种类型的实参,而不需要明确某一特定类型;但对于实参,必须具有明确的类型。
子程序定义时,指定参数的类型,这样在用PERFORM调用子程序时,系统将检查语句中实参的类型是否与其对应的形参类型兼容(参数传递时不存在类型转换)。如果类型不兼容,系统会在语法检查时输出错误信息。
指定参数类型有两种情况,第一种是指定为一般类型,并不限定为特定的某一种数据类型对于这种一般类型的参数指定,只有在运行期内才可以将实参的具体结构传递给形参,所以在子程序中,只能使用整体属性,而不能使用其具体结构。
3.功能模块
功能模块也是一种过程模块,可以由ABAP应用程序进行外部调用。功能模块基本思想与子程序类似,但具有更为广泛的适用性,且需要通过单独Workbench工具Function Builder在功能组中进行开发和维护。每一个功能模块在系统中都是唯一的,因而在调用时无需指定其隶属的功能组。
1.功能模块和功能组
功能组是一种ABAP程序,是功能模块的容器。在功能组定义时,系统将生成一个主程序,命名为ASPL<fgrp>,该主程序中含有下述包含程序:
- L<fgrp> TOP是功能组内部的全局数据定义区
- L<fgrp> UXX包含具体的功能模块代码,“XX”代表其创建先后的序号,例如L<fgrp>U01和L<fgrp>U02是头两个被创建的模块
- L<fgrp> FXX用于容纳功能组内部的子程序,如L<fgrp>F01。
2.创建与测试
(1)create function groups 事务代码SE37,goto->function groups->create group
(2)create function moudel
(3) 功能设计界面
- Attribute 用于设定功能模块的类型,名称等通用属性
- Import 用于定义输入参数
- Export 用于定义输出参数
- Changing 用于定义输入输出参数
- Tables 用于定义内表
- Exceptions 用于定义可能出现的异常
(4) 定义输入输出参数
程序的注释部分已经根据参数的设定而更改。
(5) 激活程序。注意,一定要激活function group下激活。
3.调用功能模块
在程序中使用CALL FUNCTION语句调用已经定义或系统原有的sap功能模块。
CALL FUNCTION moudle
[EXPORTING f1 = a1 ... fn = an ]
[IMPORTING f1 = a1 ... fn = an ]
[CHANGING f1 = a1 ... fn = an ]
[TABLE f1 = a1 ... fn = an ]
[EXCEPTIONS e1 = r1 ... en = rn [OTHERS = Ro] ]
通过EXPORTING,IMPORTING,TABLES,CHANGING选项与功能模块之间进行参数传递。
- EXPORTING 选项用于将实参an传递给形参fn。在功能模块定义时,必须将形参声明为输入参数。
- IMPORTING 选项允许将形参fn传递给实参an。在功能模块定义时,必须将形参声明为输入参数。
- CHANGING 选项允许将实参an传递给形参fn,在处理功能模块后,必须将(更改过的)形参fn传递返回实参an。在功能模块定义时,必须将形参声明为Changing参数,这类似于子程序参数传递中的引用传递,实参的值将被更改。
- TABLES 选项允许在实参和形参间传递内表,该选项中的内表总是以被引用的方式传递
-
EXCEPTIONS 选项可以处理功能模块程序代码中发生的异常,如果在调用时出现en异常,系统停止执行功能模块 ,并且不将任何参数值从功能模块传递给程序,仅通过将值rn分配给调用程序中的系统字段SY-SUBRC来处理异常。其中rn需指定为N类型。可以使用OTHERS选项指定列表中没有显示指定的所有其他异常,并且可将同一数值rn用于多个异常。
调用功能模块时一般不需要程序员自行指定CALL FUNCTION语句中的各个参数,利用模式生成功能。
输入功能模块名称并确定后,下列代码自动插入到程序中:
CALL FUNCTION 'Z_INTEGER_ADD'
EXPORTING
input_1 =
input_2 =
* IMPORTING
* OUTPUT =
.
编辑之后
REPORT z_calling_function.
DATA: int1 TYPE i VALUE 1,
int2 TYPE i VALUE 2,
sum TYPE i.
CALL FUNCTION 'Z_INTEGER_ADD'
EXPORTING
input_1 = int1
input_2 = int2
IMPORTING
output = sum.
IF sy-subrc = 0.
WRITE sum.
ENDIF.
4.生存期与可见性
数据对象的可见性和生存期的概念是与过程相关联的,可以在过程内部或者外部进行数据对象的声明。过程内部声明的数据对象称为局部对象,其他对象(包括事件块和对话模块中声明的对象)则称为全局对象。
1.局部数据-是在过程中创建的类型或对象,其作用域仅为过程内部。
FORM add USING p1 p2.
DATA sum TYPE i.
sum = p1 + p2.
WRITE: / 'int1 + int2 =', sum.
ENDFORM.
变量sun就是动态局部数据对象。
2.全局数据对象隐藏-如果局部数据的名称与全局数据相同,则全局数据将被屏蔽。
REPORT z_global_data_hidden.
DATA:int1 TYPE i VALUE 1,
int2 TYPE i VALUE 2,
sum TYPE i VALUE 0.
PERFORM add USING int1 int2.
WRITE: / 'Outside Sub : sum = ', sum.
FORM add USING p1 p2.
DATA sum TYPE i.
sum = p1 + p2.
WRITE: /'Inside Sub : sum = ', sum.
ENDFORM.
3.静态数据对象-如果一个数据只在一个子程序内部出现,但在子程序调用过程结束后,还需要继续保留局部数据对象的值,可以使用STATICS语句代替DATA语句在子程序中进行对象声明。
STATICS s[length] type [value] [decimals].
直接定义静态的结构体数据对象的语法为:
STATICS: BEGIN OF fstring,
<component declaration>,
...
END OF fstring.
静态数据对象的意义在于,如果在程序中多次调用某一子程序,静态变量不会像动态局部数据对象那用重新分配内存空间,系统将保留静态局部数据对象的值,而且可在下次调用同一子程序时在当前值基础上继续进行操作。
REPORT z_global_data_hidden.
DATA: int1 TYPE i VALUE 1,
int2 TYPE i VALUE 2.
PERFORM add USING int1.
PERFORM add USING int2.
FORM add USING p1.
STATICS sum TYPE i.
sum = sum + p1.
WRITE : / 'sum = ', sum.
ENDFORM.
5.源代码复用
包含程序和宏是单纯的源代码复用技术,与ABAP过程和处理块调用无关,而且也不存在参数接口,通常使用它们的目的是将程序分为几个逻辑部分或利用占位符使程序易于理解和维护。
1.包含程序-是类型为I的ABAP程序,是单纯的代码复用,只相当于把代码段插入到引用该包含程序的位置,且不能与调用它的程序进行显示数据传递。包含程序是不可执行,不能单独运行,必须被其他程序调用。
包含程序可以通过ABAP编辑器或者对象导航创建,不需要PROGRAM或REPORT语句进行引导。包含程序不能调用自身,因为可能形成无限循环,而且其中的语句必须是完整的。
*&---------------------------------------------------------------------*
*& Include Z_CLUDE_PGM
*&---------------------------------------------------------------------*
WRITE: / 'Program started by', sy-uname, 'on host', sy-host,
'data:', sy-datum, 'time:', sy-uzeit.
ULINE.
要从其他ABAP程序中调用已经创建的包含程序,使用INCLUDE语句。INCLUDE incl
。INCLUDE语句必须独占一行,并且不能像DATA语句那样使用冒号扩展到多行。包含程序不是运行时装载的,而是在程序生成前就被解析。
REPORT z_clude_demo.
INCLUDE z_clude_pgm.
SKIP.
WRITE: /'Report Output'.
2.宏-另一种单纯源代码复用技术,也不存在参数传递机制,比包含程序形式更为简单,往往只是为了增强程序的可读性(例如将数值0定义为名称为ZERO的宏)。
使用DEFINE语句定义包含源代码的宏
DEFINE macro.
<statements>
END-OF-DEFINTION.
这语句定义名为macro的宏。必须在DEFINE和END-OF-DEFINITION之间指定完整的语句。这些语句中可以包含最多九个占位符(&1, &2, ... ,&9)。完成定义后,就可以对其进行引用macro [p1 p2 ... p9]
。在生成程序期间,系统用已定义的语句替换程序中的macro,用pi替代每个占位符。可以从宏中调用另一个宏,但宏不能调用自身。
REPORT z_macro_demo.
DATA: result TYPE i,
int1 TYPE i VALUE 1,
int2 TYPE i VALUE 2.
DEFINE operation.
result = &1 &2 &3.
output &1 &2 &3 result.
END-OF-DEFINITION.
DEFINE output.
write: / 'The result of &1 &2 &3 is', &4.
END-OF-DEFINITION.
operation 1 + 2.
operation int2 - int1.