ABAP面向对象编程(OO)学习指南

一、基础概念

1.1 类与对象

类(Class)是对象的模板,定义了对象的属性和行为。对象(Object)是类的实例。

* 定义类
CLASS lcl_vehicle DEFINITION.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_type TYPE string
iv_speed TYPE i,
get_type RETURNING VALUE(rv_type) TYPE string,
get_speed RETURNING VALUE(rv_speed) TYPE i,
accelerate IMPORTING iv_increment TYPE i.
PRIVATE SECTION.
DATA: mv_type TYPE string,
mv_speed TYPE i.
ENDCLASS.

* 实现类
CLASS lcl_vehicle IMPLEMENTATION.
METHOD constructor.
mv_type = iv_type.
mv_speed = iv_speed.
ENDMETHOD.

METHOD get_type.
rv_type = mv_type.
ENDMETHOD.

METHOD get_speed.
rv_speed = mv_speed.
ENDMETHOD.

METHOD accelerate.
mv_speed = mv_speed + iv_increment.
ENDMETHOD.
ENDCLASS.

* 创建对象并使用
START-OF-SELECTION.
DATA(lo_car) = NEW lcl_vehicle( iv_type = 'Car' iv_speed = 0 ).
lo_car->accelerate( 60 ).
WRITE: / lo_car->get_type( ), lo_car->get_speed( ).

1.2 可见性区域

区域 关键字 说明
公有 PUBLIC SECTION 类内外均可访问
保护 PROTECTED SECTION 类及其子类可访问
私有 PRIVATE SECTION 仅类内部可访问

1.3 实例成员与静态成员

CLASS lcl_counter DEFINITION.
PUBLIC SECTION.
CLASS-DATA: gv_total_count TYPE i. " 静态属性(类级别)
DATA: mv_instance_count TYPE i. " 实例属性(对象级别)
CLASS-METHODS: get_total RETURNING VALUE(rv_total) TYPE i. " 静态方法
METHODS: increment. " 实例方法
ENDCLASS.

CLASS lcl_counter IMPLEMENTATION.
METHOD increment.
mv_instance_count = mv_instance_count + 1.
gv_total_count = gv_total_count + 1.
ENDMETHOD.
METHOD get_total.
rv_total = gv_total_count.
ENDMETHOD.
ENDCLASS.

1.4 CREATE 实例化控制

* CREATE PUBLIC: 任何地方均可实例化(默认)
CLASS lcl_public DEFINITION CREATE PUBLIC.
...
ENDCLASS.

* CREATE PRIVATE: 仅类内部可实例化(单例模式常用)
CLASS lcl_singleton DEFINITION CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO lcl_singleton.
PRIVATE SECTION.
CLASS-DATA: go_instance TYPE REF TO lcl_singleton.
ENDCLASS.

CLASS lcl_singleton IMPLEMENTATION.
METHOD get_instance.
IF go_instance IS NOT BOUND.
CREATE OBJECT go_instance.
ENDIF.
ro_instance = go_instance.
ENDMETHOD.
ENDCLASS.

* CREATE PROTECTED: 仅类及其子类可实例化(工厂模式常用)
CLASS lcl_factory DEFINITION CREATE PROTECTED.
...
ENDCLASS.

二、继承与多态

2.1 继承(INHERITING FROM)

* 父类: 交通工具
CLASS lcl_vehicle DEFINITION.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_brand TYPE string,
get_brand RETURNING VALUE(rv_brand) TYPE string,
describe. " 可被重写
PROTECTED SECTION.
DATA: mv_brand TYPE string.
ENDCLASS.

CLASS lcl_vehicle IMPLEMENTATION.
METHOD constructor.
mv_brand = iv_brand.
ENDMETHOD.
METHOD get_brand.
rv_brand = mv_brand.
ENDMETHOD.
METHOD describe.
WRITE: / 'This is a vehicle by', mv_brand.
ENDMETHOD.
ENDCLASS.

* 子类: 汽车(继承自交通工具)
CLASS lcl_car DEFINITION INHERITING FROM lcl_vehicle.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_brand TYPE string
iv_doors TYPE i,
describe REDEFINITION. " 重写父类方法
PRIVATE SECTION.
DATA: mv_doors TYPE i.
ENDCLASS.

CLASS lcl_car IMPLEMENTATION.
METHOD constructor.
super->constructor( iv_brand ). " 调用父类构造方法
mv_doors = iv_doors.
ENDMETHOD.
METHOD describe.
WRITE: / mv_brand, 'car with', mv_doors, 'doors'.
ENDMETHOD.
ENDCLASS.

2.2 抽象类与方法

CLASS lcl_shape DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_name TYPE string,
get_area ABSTRACT RETURNING VALUE(rv_area) TYPE f, " 抽象方法,子类必须实现
get_name RETURNING VALUE(rv_name) TYPE string.
PROTECTED SECTION.
DATA: mv_name TYPE string.
ENDCLASS.

CLASS lcl_shape IMPLEMENTATION.
METHOD constructor.
mv_name = iv_name.
ENDMETHOD.
METHOD get_name.
rv_name = mv_name.
ENDMETHOD.
ENDCLASS.

* 子类: 圆形
CLASS lcl_circle DEFINITION INHERITING FROM lcl_shape.
PUBLIC SECTION.
METHODS: constructor IMPORTING iv_name TYPE string
iv_radius TYPE f,
get_area REDEFINITION.
PRIVATE SECTION.
DATA: mv_radius TYPE f.
ENDCLASS.

CLASS lcl_circle IMPLEMENTATION.
METHOD constructor.
super->constructor( iv_name ).
mv_radius = iv_radius.
ENDMETHOD.
METHOD get_area.
rv_area = 3.14159 * mv_radius * mv_radius.
ENDMETHOD.
ENDCLASS.

2.3 最终类(FINAL)

CLASS lcl_utility DEFINITION FINAL.
PUBLIC SECTION.
CLASS-METHODS: format_date IMPORTING iv_date TYPE d
RETURNING VALUE(rv_formatted) TYPE string.
ENDCLASS.

CLASS lcl_utility IMPLEMENTATION.
METHOD format_date.
rv_formatted = |{ iv_date+0(4) }-{ iv_date+4(2) }-{ iv_date+6(2) }|.
ENDMETHOD.
ENDCLASS.

2.4 多态与向上转型

DATA: lo_shape TYPE REF TO lcl_shape.

* 子类对象赋给父类引用(向上转型)
lo_shape = NEW lcl_circle( iv_name = 'Circle1' iv_radius = 5 ).
WRITE: / lo_shape->get_name( ), lo_shape->get_area( ). " 运行时决定调用哪个get_area

三、接口(Interfaces)

3.1 定义与实现

* 定义接口
INTERFACE lif_printable.
METHODS: print RETURNING VALUE(rv_text) TYPE string.
ENDINTERFACE.

INTERFACE lif_serializable.
METHODS: serialize RETURNING VALUE(rv_json) TYPE string,
deserialize IMPORTING iv_json TYPE string.
ENDINTERFACE.

* 实现多个接口
CLASS lcl_report DEFINITION.
PUBLIC SECTION.
INTERFACES: lif_printable, lif_serializable. " 实现两个接口
METHODS: constructor IMPORTING iv_title TYPE string.
PRIVATE SECTION.
DATA: mv_title TYPE string.
ENDCLASS.

CLASS lcl_report IMPLEMENTATION.
METHOD constructor.
mv_title = iv_title.
ENDMETHOD.
METHOD lif_printable~print.
rv_text = |Report: { mv_title }|.
ENDMETHOD.
METHOD lif_serializable~serialize.
rv_json = |\{"title":"{ mv_title }"\}|.
ENDMETHOD.
METHOD lif_serializable~deserialize.
" 简化解析
mv_title = 'Deserialized Report'.
ENDMETHOD.
ENDCLASS.

3.2 接口引用与多态

DATA: lo_printable TYPE REF TO lif_printable.

lo_printable = NEW lcl_report( 'Sales Report' ).
WRITE: / lo_printable->print( ). " 通过接口调用

* 检查对象是否实现某接口
DATA(lo_report) = NEW lcl_report( 'Test' ).
IF lo_report IS INSTANCE OF lif_serializable.
DATA(lo_serial) = CAST lif_serializable( lo_report ).
WRITE: / lo_serial->serialize( ).
ENDIF.

3.3 接口组合(嵌套接口)

INTERFACE lif_component.
INTERFACES: lif_printable, lif_serializable.
" 组合接口,实现者需实现所有嵌套接口的方法
ENDINTERFACE.

四、事件编程

4.1 定义与触发事件

CLASS lcl_order DEFINITION.
PUBLIC SECTION.
EVENTS: order_created EXPORTING VALUE(ev_order_id) TYPE string,
status_changed EXPORTING VALUE(ev_new_status) TYPE string.
METHODS: constructor IMPORTING iv_order_id TYPE string,
set_status IMPORTING iv_status TYPE string.
PRIVATE SECTION.
DATA: mv_order_id TYPE string,
mv_status TYPE string.
ENDCLASS.

CLASS lcl_order IMPLEMENTATION.
METHOD constructor.
mv_order_id = iv_order_id.
mv_status = 'NEW'.
RAISE EVENT order_created EXPORTING ev_order_id = mv_order_id.
ENDMETHOD.
METHOD set_status.
mv_status = iv_status.
RAISE EVENT status_changed EXPORTING ev_new_status = mv_status.
ENDMETHOD.
ENDCLASS.

4.2 事件处理

CLASS lcl_order_monitor DEFINITION.
PUBLIC SECTION.
METHODS: on_order_created FOR EVENT order_created OF lcl_order
IMPORTING ev_order_id sender,
on_status_changed FOR EVENT status_changed OF lcl_order
IMPORTING ev_new_status sender.
ENDCLASS.

CLASS lcl_order_monitor IMPLEMENTATION.
METHOD on_order_created.
WRITE: / 'Order created:', ev_order_id.
ENDMETHOD.
METHOD on_status_changed.
WRITE: / 'Status changed to:', ev_new_status.
ENDMETHOD.
ENDCLASS.

* 注册事件处理器
START-OF-SELECTION.
DATA(lo_monitor) = NEW lcl_order_monitor( ).
DATA(lo_order) = NEW lcl_order( 'ORD001' ).

SET HANDLER lo_monitor->on_order_created FOR lo_order.
SET HANDLER lo_monitor->on_status_changed FOR lo_order.

lo_order->set_status( 'PROCESSING' ).
lo_order->set_status( 'COMPLETED' ).

4.3 静态事件

CLASS lcl_system_event DEFINITION.
PUBLIC SECTION.
CLASS-EVENTS: system_alert IMPORTING iv_message TYPE string.
CLASS-METHODS: trigger_alert IMPORTING iv_message TYPE string.
ENDCLASS.

CLASS lcl_system_event IMPLEMENTATION.
METHOD trigger_alert.
RAISE EVENT system_alert EXPORTING iv_message = iv_message.
ENDMETHOD.
ENDCLASS.

五、高级特性

5.1 RTTI 运行时类型识别

DATA: lo_type TYPE REF TO cl_abap_typedescr.

* 获取类型描述
lo_type = cl_abap_typedescr=>describe_by_data( sy-datum ).
WRITE: / 'Type:', lo_type->type_kind,
/ 'Kind:', lo_type->kind,
/ 'Length:', lo_type->length.

* 获取类的属性列表
DATA(lo_class) = CAST cl_abap_classdescr(
cl_abap_typedescr=>describe_by_name( 'LCL_VEHICLE' ) ).

LOOP AT lo_class->attributes INTO DATA(ls_attr).
WRITE: / ls_attr-name, ls_attr-is_class, ls_attr-is_read_only.
ENDLOOP.

* 动态创建对象
DATA: lo_dynamic TYPE REF TO object.
CREATE OBJECT lo_dynamic TYPE ('LCL_VEHICLE')
EXPORTING iv_brand = 'Dynamic BMW'.

5.2 强制类型转换

DATA: lo_vehicle TYPE REF TO lcl_vehicle,
lo_car TYPE REF TO lcl_car.

lo_car = NEW lcl_car( iv_brand = 'BMW' iv_doors = 4 ).

* 向上转型(安全,隐式)
lo_vehicle = lo_car.

* 向下转型(需要显式转换)
TRY.
lo_car = CAST lcl_car( lo_vehicle ). " 安全转换
WRITE: / lo_car->get_brand( ).
CATCH cx_sy_move_cast_error INTO DATA(lx_error).
WRITE: / 'Cast error:', lx_error->get_text( ).
ENDTRY.

5.3 泛型(Generics)

* 泛型类示例
CLASS lcl_stack DEFINITION.
PUBLIC SECTION.
METHODS: push IMPORTING iv_value TYPE any,
pop EXPORTING ev_value TYPE any,
is_empty RETURNING VALUE(rv_empty) TYPE abap_bool.
PRIVATE SECTION.
DATA: mt_data TYPE REF TO data.
ENDCLASS.

CLASS lcl_stack IMPLEMENTATION.
METHOD push.
" 使用动态数据对象
FIELD-SYMBOLS: <lt_data> TYPE ANY TABLE.
ASSIGN mt_data->* TO <lt_data>.
INSERT iv_value INTO TABLE <lt_data>.
ENDMETHOD.
METHOD pop.
FIELD-SYMBOLS: <lt_data> TYPE ANY TABLE.
ASSIGN mt_data->* <lt_data>.
READ TABLE <lt_data> INDEX 1 INTO ev_value.
DELETE <lt_data> INDEX 1.
ENDMETHOD.
METHOD is_empty.
FIELD-SYMBOLS: <lt_data> TYPE ANY TABLE.
ASSIGN mt_data->* TO <lt_data>.
rv_empty = COND #( WHEN <lt_data> IS INITIAL THEN abap_true ELSE abap_false ).
ENDMETHOD.
ENDCLASS.

六、设计模式实践

6.1 工厂模式(Factory Pattern)

INTERFACE lif_vehicle.
METHODS: get_type RETURNING VALUE(rv_type) TYPE string.
ENDINTERFACE.

CLASS lcl_car DEFINITION.
PUBLIC SECTION.
INTERFACES lif_vehicle.
ENDCLASS.
CLASS lcl_car IMPLEMENTATION.
METHOD lif_vehicle~get_type.
rv_type = 'Car'.
ENDMETHOD.
ENDCLASS.

CLASS lcl_truck DEFINITION.
PUBLIC SECTION.
INTERFACES lif_vehicle.
ENDCLASS.
CLASS lcl_truck IMPLEMENTATION.
METHOD lif_vehicle~get_type.
rv_type = 'Truck'.
ENDMETHOD.
ENDCLASS.

CLASS lcl_vehicle_factory DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: create IMPORTING iv_type TYPE string
RETURNING VALUE(ro_vehicle) TYPE REF TO lif_vehicle.
ENDCLASS.

CLASS lcl_vehicle_factory IMPLEMENTATION.
METHOD create.
ro_vehicle = SWITCH #( iv_type
WHEN 'CAR' THEN NEW lcl_car( )
WHEN 'TRUCK' THEN NEW lcl_truck( ) ).
ENDMETHOD.
ENDCLASS.

6.2 策略模式(Strategy Pattern)

INTERFACE lif_discount_strategy.
METHODS: calculate IMPORTING iv_price TYPE p
RETURNING VALUE(rv_final) TYPE p.
ENDCLASS.

CLASS lcl_no_discount DEFINITION.
PUBLIC SECTION.
INTERFACES lif_discount_strategy.
ENDCLASS.
CLASS lcl_no_discount IMPLEMENTATION.
METHOD lif_discount_strategy~calculate.
rv_final = iv_price.
ENDMETHOD.
ENDCLASS.

CLASS lcl_percentage_discount DEFINITION.
PUBLIC SECTION.
INTERFACES lif_discount_strategy.
METHODS: constructor IMPORTING iv_percent TYPE p.
PRIVATE SECTION.
DATA: mv_percent TYPE p.
ENDCLASS.
CLASS lcl_percentage_discount IMPLEMENTATION.
METHOD constructor.
mv_percent = iv_percent.
ENDMETHOD.
METHOD lif_discount_strategy~calculate.
rv_final = iv_price * ( 1 - mv_percent / 100 ).
ENDMETHOD.
ENDCLASS.

CLASS lcl_shopping_cart DEFINITION.
PUBLIC SECTION.
METHODS: set_strategy IMPORTING io_strategy TYPE REF TO lif_discount_strategy,
checkout IMPORTING iv_price TYPE p RETURNING VALUE(rv_final) TYPE p.
PRIVATE SECTION.
DATA: mo_strategy TYPE REF TO lif_discount_strategy.
ENDCLASS.
CLASS lcl_shopping_cart IMPLEMENTATION.
METHOD set_strategy.
mo_strategy = io_strategy.
ENDMETHOD.
METHOD checkout.
rv_final = mo_strategy->calculate( iv_price ).
ENDMETHOD.
ENDCLASS.

6.3 观察者模式(Observer Pattern)

INTERFACE lif_observer.
METHODS: on_update IMPORTING iv_data TYPE string.
ENDINTERFACE.

CLASS lcl_subject DEFINITION.
PUBLIC SECTION.
METHODS: attach IMPORTING io_observer TYPE REF TO lif_observer,
detach IMPORTING io_observer TYPE REF TO lif_observer,
notify IMPORTING iv_data TYPE string.
PRIVATE SECTION.
DATA: mt_observers TYPE TABLE OF REF TO lif_observer.
ENDCLASS.

CLASS lcl_subject IMPLEMENTATION.
METHOD attach.
APPEND io_observer TO mt_observers.
ENDMETHOD.
METHOD detach.
DELETE mt_observers WHERE table_line = io_observer.
ENDMETHOD.
METHOD notify.
LOOP AT mt_observers INTO DATA(lo_obs).
lo_obs->on_update( iv_data ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

七、ABAP Unit 测试

7.1 编写测试类

CLASS lcl_calculator DEFINITION.
PUBLIC SECTION.
METHODS: add IMPORTING iv_a TYPE i iv_b TYPE i RETURNING VALUE(rv_result) TYPE i,
divide IMPORTING iv_a TYPE i iv_b TYPE i RETURNING VALUE(rv_result) TYPE p
RAISING cx_sy_zerodivide.
ENDCLASS.

CLASS lcl_calculator IMPLEMENTATION.
METHOD add.
rv_result = iv_a + iv_b.
ENDMETHOD.
METHOD divide.
IF iv_b = 0.
RAISE EXCEPTION TYPE cx_sy_zerodivide.
ENDIF.
rv_result = iv_a / iv_b.
ENDMETHOD.
ENDCLASS.

* 测试类
CLASS ltc_calculator DEFINITION FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA: mo_cut TYPE REF TO lcl_calculator. " Class Under Test
METHODS: setup,
test_add FOR TESTING,
test_divide FOR TESTING,
test_divide_by_zero FOR TESTING.
ENDCLASS.

CLASS ltc_calculator IMPLEMENTATION.
METHOD setup.
mo_cut = NEW lcl_calculator( ).
ENDMETHOD.
METHOD test_add.
cl_abap_unit_assert=>assert_equals(
act = mo_cut->add( iv_a = 2 iv_b = 3 )
exp = 5
msg = 'Addition should return 5' ).
ENDMETHOD.
METHOD test_divide.
cl_abap_unit_assert=>assert_equals(
act = mo_cut->divide( iv_a = 10 iv_b = 2 )
exp = 5
msg = 'Division should return 5' ).
ENDMETHOD.
METHOD test_divide_by_zero.
TRY.
mo_cut->divide( iv_a = 10 iv_b = 0 ).
cl_abap_unit_assert=>fail( 'Should have raised cx_sy_zerodivide' ).
CATCH cx_sy_zerodivide.
" Expected
ENDTRY.
ENDMETHOD.
ENDCLASS.

7.2 测试断言方法

* 常用断言
cl_abap_unit_assert=>assert_equals( act = lv_act exp = lv_exp ).
cl_abap_unit_assert=>assert_differs( act = lv_act exp = lv_exp ).
cl_abap_unit_assert=>assert_initial( act = lv_value ).
cl_abap_unit_assert=>assert_not_initial( act = lv_value ).
cl_abap_unit_assert=>assert_true( act = lv_bool ).
cl_abap_unit_assert=>assert_false( act = lv_bool ).
cl_abap_unit_assert=>assert_number_between( lower = 1 upper = 100 number = lv_num ).

八、BAdI 与增强框架

8.1 经典 BAdI

* 定义 BAdI (SE19)
INTERFACE lif_custom_logic.
METHODS: execute IMPORTING iv_input TYPE string
RETURNING VALUE(rv_output) TYPE string.
ENDINTERFACE.

* BAdI 实现
CLASS lcl_custom_impl DEFINITION.
PUBLIC SECTION.
INTERFACES lif_custom_logic.
ENDCLASS.

CLASS lcl_custom_impl IMPLEMENTATION.
METHOD lif_custom_logic~execute.
rv_output = |Processed: { iv_input }|.
ENDMETHOD.
ENDCLASS.

* 调用 BAdI
DATA: lo_badi TYPE REF TO lif_custom_logic.
GET BADI lo_badi.
CALL BADI lo_badi->execute
EXPORTING iv_input = 'Test'
RECEIVING rv_output = DATA(lv_result).

8.2 新式 BAdI (Enhancement Spot)

* 使用增强点
DATA: lo_badi TYPE REF TO if_custom_logic.

TRY.
GET BADI lo_badi.
CALL BADI lo_badi->execute
EXPORTING iv_input = 'Data'
RECEIVING rv_output = DATA(lv_output).
CATCH cx_badi_not_implemented.
" 无实现时的默认处理
ENDTRY.

九、与现代ABAP框架集成

9.1 CDS View 实体

* 定义 CDS View
@AbapCatalog.sqlViewName: 'ZSALESV'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order View'
define view Z_SALES_ORDER
as select from vbak
{
key vbeln as OrderNumber,
erdat as CreatedOn,
ernam as CreatedBy,
netwr as NetValue,
waerk as Currency
}
where netwr > 0;

9.2 RAP (RESTful ABAP Programming)

* Behavior Definition
managed implementation in class zbp_sales_order unique;

define behavior for Z_SALES_ORDER alias SalesOrder
{
create;
update;
delete;
validation check_amount on save { field NetValue; }
action set_status parameter Z_STATUS_PARAM result [1] $result;
}

* Behavior Implementation
CLASS zbp_sales_order DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF Z_SALES_ORDER.
ENDCLASS.

CLASS zbp_sales_order IMPLEMENTATION.
ENDCLASS.

十、最佳实践总结

10.1 SOLID 原则在 ABAP 中的应用

原则 含义 ABAP 实践
S - 单一职责 一个类只做一件事 业务类与数据访问类分离
O - 开闭原则 对扩展开放,对修改关闭 使用接口+BAdI扩展
L - 里氏替换 子类可替换父类 避免重写改变方法语义
I - 接口隔离 接口应小而专 拆分大接口为多个小接口
D - 依赖倒置 依赖抽象而非具体 通过接口引用而非具体类

10.2 编码规范

  1. 命名约定

    • 类: lcl_xxx (本地) / zcl_xxx (全局)
    • 接口: lif_xxx (本地) / zif_xxx (全局)
    • 方法: get_xxx / set_xxx / is_xxx (返回布尔)
    • 属性: mv_xxx (实例) / gv_xxx (全局静态)
  2. 构造方法

    • 参数不超过 5 个,过多时使用结构体
    • 必要参数放 constructor,可选参数用 setter
  3. 错误处理

    • 使用异常类(cx_xxx)而非返回码
    • 异常应携带有意义的消息
  4. 测试覆盖

    • 每个公共方法至少一个测试用例
    • 边界条件和异常路径必须测试

学习资源推荐

  1. SAP 官方文档: help.sap.com → ABAP Objects
  2. SAP Press:《ABAP Objects: ABAP Object-Oriented Programming》
  3. openSAP 课程: “Writing Testable ABAP Code”、”ABAP Development for SAP HANA”
  4. 实践建议: 从改造传统 ABAP 程序开始,逐步引入类和接口
  5. 社区: SAP Community Network (SCN) 的 ABAP 板块