ABAP 面向对象与其他语言的区别
引言
还记得在学习 ABAP 开发时,学到面向对象 ALV 的时候,就没耐心继续往下学习了,直到在开发过程中踩过无数坑之后,才想起 OOP(面向对象编程) 的威力。
OO ABAP 和 JAVA 是面向对象的编程风格。 ABAP/4 被扩展以支持 OO(面向对象) 概念。 Java、C++ 等面向对象的概念在 OO ABAP 中被采用,但 ABAP 也会有一些特有的特性,这意味着 ABAP 中有一些 JAVA 不支持的功能,反之亦然。需要考虑的另一个重要方面是 ABAP 语言用于商业软件开发。存在两种情况,ABAP 实现面向对象的方式不同于您从其他面向对象编程语言(如 C++、Java 或 C#)中了解到的方式。 你可以称这些为“限制”,但实际上,这些情况并没有真正降低语言的能力,只是让语法更冗长了一点。
相似之处:OO ABAP 和 JAVA 等其他面向对象语言一样,满足三大特点:继承、多态、封装。
那么不同之处呢?
OO ABAP 没有重载功能
重载意味着一个类提供多个名称完全相同但参数略有不同的方法。
例如,您可以有一个接收字符串输入参数的方法日志变体,以及另一个接收整数的方法变体。 编译器分析调用方法的方式,并引导调用正确的方法。
虽然重载是 Java 中的常见模式,但 ABAP 不支持它并强制每个方法名称在类中只出现一次。 这种限制纯粹是语法上的,不会降低语言的表达能力。 例如,ABAP 会强制您将 LOG_STRING
方法与 LOG_INTEGER
方法区分开来,从而降低代码的可读性,但我们仍然能够记录字符串和整数。
人们有时会争辩说 OPTIONAL 输入参数可以弥补过载的不足。 然而,这种说法只是部分正确。 重载使您能够表达哪些参数组合构成有效输入。 可选参数还允许组合没有意义的输入参数。 例如,一个带有两个可选输入参数 STRING 和 INTEGER 的 LOG 方法可以同时使用这两个参数调用,或者根本不调用 - 这两种情况方法的开发人员可能甚至没有考虑过并且没有进行单元测试,因此会导致意想不到的结果。
构造函数概念不同
构造函数概念在 OO ABAP 中的处理方式非常不同。
CLASS zcl_vendor DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
DATA gv_lifnr TYPE lifnr READ-ONLY.
METHODS constructor
IMPORTING
!iv_lifnr TYPE lifnr.
METHODS get_banlance
RETURNING
VALUE(rv_balance) TYPE dmbtr.
PROTECTED SECTION.
PRIVATE SECTION.
DATA:
gv_balance TYPE dmbtr,
gv_balance_read TYPE abap_bool.
ENDCLASS.
CLASS zcl_vendor IMPLEMENTATION.
METHOD constructor.
" check if iv_lifnr exists in lfa1 and raise error if not
gv_lifnr = iv_lifnr.
ENDMETHOD.
METHOD get_banlance.
IF gv_balance_read EQ abap_false.
" read vendor balance from bsik, bsak, etc into gv_balance
gv_balance_read = abap_true.
ENDIF.
rv_balance = gv_balance.
ENDMETHOD.
ENDCLASS.
OO ABAP 中的类/接口有着独特的事件
OO ABAP 中的类/接口以及属性和函数都有事件。事件概念是 OO ABAP的一个重要特征,其中一个对象引发事件;根据开发要求,数据变更、数据修改、搜索帮助时用到。
类定义如下:
CLASS lcl_event_receiver DEFINITION.
PUBLIC SECTION.
METHODS data_changed FOR EVENT data_changed OF cl_gui_alv_grid
IMPORTING er_data_changed
e_onf4
e_onf4_before
e_onf4_after
e_ucomm.
METHODS handle_modify
FOR EVENT data_changed_finished OF cl_gui_alv_grid
IMPORTING e_modified et_good_cells.
METHODS handle_f4 FOR EVENT onf4 OF cl_gui_alv_grid
IMPORTING e_fieldname
es_row_no
er_event_data
et_bad_cells
e_display.
ENDCLASS. "LCL_EVENT_RECEIVER DEFINITION
方法实现如下:
CLASS lcl_event_receiver IMPLEMENTATION.
METHOD data_changed.
PERFORM handle_data_changed USING er_data_changed.
ENDMETHOD.
METHOD handle_modify.
PERFORM handle_data_finish USING e_modified et_good_cells.
ENDMETHOD. "HANDLE_MODIFY
METHOD handle_f4.
FIELD-SYMBOLS: <fs_alv> LIKE gs_out.
CASE e_fieldname.
" 写自己的逻辑
ENDCASE.
* 设置后,alv稳定刷新
PERFORM refresh_table_alv.
ENDMETHOD.
ENDCLASS. "LCL_EVENT_RECEIVER IMPLEMENTATION
FORM refresh_table_alv .
gs_stbl-row = 'X'." 基于行的稳定刷新
gs_stbl-col = 'X'." 基于列稳定刷新
CALL METHOD gcl_alv_grid->refresh_table_display
EXPORTING
is_stable = gs_stbl.
ENDFORM. " REFRESH_TABLE_ALV
具体函数 HANDLE_DATA_CHANGED
:
*&---------------------------------------------------------------------*
*& Form HANDLE_DATA_CHANGED
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* -->P_ER_DATA_CHANGED text
*----------------------------------------------------------------------*
FORM handle_data_changed USING p_er_data_changed
TYPE REF TO cl_alv_changed_data_protocol.
DATA: mod_data TYPE lvc_t_modi,
gs_out_mod_data TYPE lvc_s_modi.
mod_data = p_er_data_changed->mt_mod_cells.
LOOP AT mod_data INTO gs_out_mod_data.
ENDLOOP.
ENDFORM.
具体函数 handle_data_finish
,如下:
*&---------------------------------------------------------------------*
*& Form HANDLE_DATA_FINISH
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* -->P_E_MODIFIED text
* -->P_ET_GOOD_CELLS text
*----------------------------------------------------------------------*
FORM handle_data_finish USING e_modified TYPE char01
et_good_cells TYPE lvc_t_modi.
DATA: eh_good_cells TYPE lvc_s_modi.
IF e_modified = 'X'.
LOOP AT et_good_cells INTO eh_good_cells. " 修改的行
UNASSIGN: <f_out>.
READ TABLE gt_out ASSIGNING <f_out> INDEX eh_good_cells-row_id.
IF sy-subrc = 0.
CLEAR: gs_out.
LOOP AT gt_out INTO gs_out WHERE asnum = <f_out>-asnum.
" 显示当前登录id
CALL METHOD cl_gui_frontend_services=>get_user_name
CHANGING
user_name = c_str
EXCEPTIONS
cntl_error = 1
error_no_gui = 2
not_supported_by_gui = 3
OTHERS = 4.
CALL METHOD cl_gui_cfw=>flush
EXCEPTIONS
cntl_system_error = 1
cntl_error = 2.
CALL 'ThUsrInfo' ID 'OPCODE' FIELD opcode_usr_attr
ID'TERMINAL' FIELD terminal.
gs_out-erdat = sy-datum.
gs_out-erzet = sy-uzeit.
gs_out-ernam = c_str.
gs_out-zlogin = terminal.
CASE eh_good_cells-fieldname.
WHEN ''. " 写实际逻辑
WHEN OTHERS.
ENDCASE.
ENDLOOP.
ENDIF.
ENDLOOP.
" 稳定刷新
PERFORM refresh_table_alv.
ENDIF.
ENDFORM.
OO ABAP 没有容器或者集合
ABAP 没有 C++ 中 STL 的容器,也没有 Java 中强大的集合。
据我所知,最大的区别——集合(ArrayList
、Vector
、LinkedList
、Queue
、Hashtable
、TreeMap
、LinkedHashMap
、LinkedHashSet
、TreeSet
)在 Java 中扮演着重要的角色;
OO ABAP 中没有同样的功能。如果我们需要集合的时候,例如处理一堆数据,就可以使用内部表(internal table, 简称 it)和工作区( work area,简称 wa)。
接口名称成为方法名称的一部分
当一个类实现一个接口时,该类通常会完全“接管”接口的方法,代码看起来就像这些方法是该类的原始片段一样。
例如,在 Java 中,如果类 Person
实现了指定一个名为 getEmail
的方法的 BusinessPartner
接口,您可以这样写:
Person alex = new Person();
alex.getEmail();
在 ABAP 中,从接口接管的方法会添加接口名称作为前缀。 对于前面的示例,ABAPer 需要编写:
DATA(bob) = NEW person( );
bob->business_partner~get_email( ).
如果一个类一次实现多个接口,此模式可避免名称冲突。
但是,请注意,如果变量的类型清楚地表明了预期的方法,则不需要接口的名称,如:
DATA charis TYPE REF TO business_partner.
charis = NEW person( ).
charis->get_email( ).
同样,这个事实并不是限制,因为您仍然可以让一个类完美地实现多个接口。 ABAP 的方法调用可能看起来只比其他编程语言长一点。
- 点赞
- 收藏
- 关注作者
评论(0)