* 定义类 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_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.
* 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.
* 父类: 交通工具 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_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_utility DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS: format_date IMPORTING iv_date TYPE d RETURNING VALUE(rv_formatted) TYPE string. ENDCLASS.
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_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_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_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.
* 定义 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 编码规范
命名约定
类: lcl_xxx (本地) / zcl_xxx (全局)
接口: lif_xxx (本地) / zif_xxx (全局)
方法: get_xxx / set_xxx / is_xxx (返回布尔)
属性: mv_xxx (实例) / gv_xxx (全局静态)
构造方法
参数不超过 5 个,过多时使用结构体
必要参数放 constructor,可选参数用 setter
错误处理
使用异常类(cx_xxx)而非返回码
异常应携带有意义的消息
测试覆盖
每个公共方法至少一个测试用例
边界条件和异常路径必须测试
学习资源推荐
SAP 官方文档: help.sap.com → ABAP Objects
SAP Press:《ABAP Objects: ABAP Object-Oriented Programming》
openSAP 课程: “Writing Testable ABAP Code”、”ABAP Development for SAP HANA”