通过接口标准化ABAP OO开发

本文是对接口编程的讨论,希望能对年轻的开发者有所帮助。

要点:

  • 通过接口对类方法进行更高层的抽象
  • 接口使代码清晰易读
  • 接口使你可以创建模拟对象(Mockup Object)以提高代码的可测试性
  • 帮助实现SOLID原则
  • 可以在不使用RTTS和类型转换的前提下使用多种类的不同实例。

因为在学习ABAP之前,我曾经学习过其它面向对象语言,因此我很纠结于ABAP中不存在的一个特性——重载方法(overload)。

也许你会问,重载是什么?

重载就是函数或者方法有相同的名称,但是参数列表和实现不相同的情形。

没有了重载,在某种程度上,类也许会变的过大,并且难以追踪那些有着相似行为但是名字不同的方法。

接口不提供重载能力,但是通过限制名字不同但是功能相近的方法的数量,接口可以整理和简化你的代码。

本文链接:http://www.cnblogs.com/hhelibeb/p/8919767.html

英文原文:Using interfaces to standardize your ABAP OO Development

简介

在ABAP中类的继承是单一继承(每个类只能有一个父类),接口实现可以有多个。

例如,上图中的LCL_Child_Class继承LCL_Parent_Class中所有的非私有变量、方法、类型和常量,并且必须实现LINF_Utility和LINF_Saver接口中所有的功能。

为了解释接口的定义,我将使用个“不怎么专业”的描述——它是一个类似于类的实体,不包含所声明的方法的任何具体实现,但是它可能包含常量、类型和变量。接口无法被初始化。

默认情况下接口的所有方法都必须被实现——这是面向对象编程中的一个通常的强制规则。不能允许接口方法的实现变得可选择,但是这不属于本文的讨论范围,所以不会展开论述。

(译注:原文评论指出,在ABAP中,可以使用DEFAULT IGNORE|FAIL附加项指定一个可选的接口方法,虽然好像并没有什么用)

“真实”用例

设想下我们有个程序,需要从多种数据源获取数据并更新到表SFLIGHT:

  • Excel上传
  • RFC上传
  • 在程序运行期间上传修改和插入的行

当然我们可以在该清单中添加ADBC源、经由HTTP客户端对象抓取的JSON/XML源等,但是我只是想介绍下要点,没必要穷举所有例子。

同时,因为本文只是对可能性的表述,因此我不会创建一个能真正工作的程序。

声明接口

我们将创建2个接口,不过在这个例子里只有一个是真实需要的。

第一个是最重要的,我命名它为linf_sflight_career,因为这是个用于EXCEL、RFC和本地表运输(carrier)的本地接口,在本地类中实现。

interface linf_sflight_carrier.
    types: tt_sflight type standard table of sflight with default key,
           st_sflight type sorted table of sflight with non-unique key mandt carrid connid,
           ht_sflight type hashed table of sflight with unique key mandt carrid connid fldate.
    methods: "! Returns hashed table SFLIGHT contents
             "! @parameter r_sflight |
             get_hashed_records returning value(r_sflight) type ht_sflight,
             "! Returns sorted table SFLIGHT contents
             "! @parameter r_sflight |
             get_sorted_records returning value(r_sflight) type st_sflight,
             "! Returns standard table SFLIGHT contents
             "! @parameter r_sflight |
             get_standard_records returning value(r_sflight) type tt_sflight.
endinterface.

接口包含不同的表类型和三个方法,将会在EXCEL、RFC和表运输的类中实现。

下个接口由负责保存数据到数据库的类实现:

interface linf_sflight_saver.
    constants: "! Table lock types
               begin of lock_types,
                exclusive type enqmode value ‘E‘,
               end of lock_types.
    constants: "! Scopes for table lock
               begin of scope_range,
                _2 type char01 value ‘2‘,
               end of scope_range.
    constants: _sflight type tablename value ‘SFLIGHT‘.
    methods: "! Save data from carrier object to SFLIGHT table
             "! @parameter i_carrier | Carrier object
             save_data importing i_carrier type ref to linf_sflight_carrier.
endinterface.

在这里,你也许会问,为什么我们需要这么多类来完成一个很简单的工作?为什么我们不利用相似的类继承或者是单个类来实现目的?

答案是显然的:SOLID。如果你想要知道关于它的更多信息,可以留言回复,我将创建另一篇博客单独讲这一话题。

回到主题——接下来是类:

class lcl_excel_carrier definition.
    public section.
        interfaces: linf_sflight_carrier.
        aliases: tt_sflight for linf_sflight_carrier~tt_sflight,
                 st_sflight for linf_sflight_carrier~st_sflight,
                 ht_sflight for linf_sflight_carrier~ht_sflight,
                 get_hashed_records for linf_sflight_carrier~get_hashed_records,
                 get_sorted_records for linf_sflight_carrier~get_sorted_records,
                 get_standard_records for linf_sflight_carrier~get_standard_records.
    protected section.
    private section.
        data: standard_sflight type tt_sflight,
              sorted_sflight type st_sflight,
              hashed_sflight type ht_sflight.
endclass.
class lcl_excel_carrier implementation.
  method get_hashed_records.
    r_sflight = hashed_sflight.
  endmethod.
  method get_sorted_records.
    r_sflight = sorted_sflight.
  endmethod.
  method get_standard_records.
    r_sflight = standard_sflight.
  endmethod.
endclass.

class lcl_rfc_carrier definition.
    public section.
            interfaces: linf_sflight_carrier.
            aliases: tt_sflight for linf_sflight_carrier~tt_sflight,
                     st_sflight for linf_sflight_carrier~st_sflight,
                     ht_sflight for linf_sflight_carrier~ht_sflight,
                     get_hashed_records for linf_sflight_carrier~get_hashed_records,
                     get_sorted_records for linf_sflight_carrier~get_sorted_records,
                     get_standard_records for linf_sflight_carrier~get_standard_records.
    protected section.
    private section.
        data: standard_sflight type tt_sflight,
              sorted_sflight type st_sflight,
              hashed_sflight type ht_sflight.
endclass.
class lcl_rfc_carrier implementation.
  method get_hashed_records.
    r_sflight = hashed_sflight.
  endmethod.
  method get_sorted_records.
    r_sflight = sorted_sflight.
  endmethod.
  method get_standard_records.
    r_sflight = standard_sflight.
  endmethod.
endclass.

class lcl_table_carrier definition.
    public section.
            interfaces: linf_sflight_carrier.
            aliases: tt_sflight for linf_sflight_carrier~tt_sflight,
                     st_sflight for linf_sflight_carrier~st_sflight,
                     ht_sflight for linf_sflight_carrier~ht_sflight,
                     get_hashed_records for linf_sflight_carrier~get_hashed_records,
                     get_sorted_records for linf_sflight_carrier~get_sorted_records,
                     get_standard_records for linf_sflight_carrier~get_standard_records.
    protected section.
    private section.
        data: standard_sflight type tt_sflight,
              sorted_sflight type st_sflight,
              hashed_sflight type ht_sflight.
endclass.
class lcl_table_carrier implementation.
  method get_hashed_records.
    r_sflight = hashed_sflight.
  endmethod.
  method get_sorted_records.
    r_sflight = sorted_sflight.
  endmethod.
  method get_standard_records.
    r_sflight = standard_sflight.
  endmethod.
endclass.

上面的类有着相同的功能,但是根据具体的运输目的,完整的实现类会有某些特定的方法(比如从raw数据中过滤、检索数据等等)。

所有运输类需要实现linf_sflight_carrier——由此我们不再不得不在每个类中定义所有的方法了。不过,我使用aliases关键字增加了别名,以提高代码的可读性。

我们下一个将要创建的类是数据库保存者,名字是lcl_database_saver:

class lcl_database_saver definition.
    public section.
        interfaces: linf_sflight_saver.
        aliases: lock_types for linf_sflight_saver~lock_types,
                 scope_range for linf_sflight_saver~scope_range,
                 save_data for linf_sflight_saver~save_data,
                 _sflight for linf_sflight_saver~_sflight.
    protected section.
    private section.
        methods: "! Creates table lock key for database lock
                 "! @parameter i_sflight_ref | Reference to SFLIGHT table line
                 "! @parameter r_varkey | Varkey returned
                 create_varkey importing i_sflight_ref type ref to sflight
                               returning value(r_varkey) type vim_enqkey,
                 "! Locks table using passed varkey
                 "! @parameter i_varkey | Table lock key
                 "! @parameter i_tabname | Table name
                 "! @parameter r_subrc | Information on lock creation. 0 = okay
                 lock_table_line importing i_varkey type vim_enqkey
                                           i_tabname type tablename default _sflight
                                 returning value(r_is_locked) type abap_bool,
                 "! Unlocks locked table line
                 "! @parameter i_varkey | Table lock key
                 "! @parameter i_tabname | Table name
                 unlock_table_line importing i_varkey type vim_enqkey
                                             i_tabname type tablename default _sflight.
endclass.
class lcl_database_saver implementation.
  method save_data.
        loop at i_carrier->get_standard_records( ) reference into data(standard_line).
            data(varkey) = create_varkey( standard_line ).
            if lock_table_line( i_varkey = varkey ).
                modify sflight from standard_line->*.
                unlock_table_line( exporting i_varkey  = varkey ).
            endif.
        endloop.
  endmethod.
  method lock_table_line.
    call function ‘ENQUEUE_E_TABLEE‘
      exporting
        mode_rstable   = lock_types-exclusive    " Lock mode for table RSTABLE
        tabname        = i_tabname    " 01th enqueue argument
        varkey         = i_varkey    " 02th enqueue argument
        _scope         = scope_range-_2
      exceptions
        foreign_lock   = 1
        system_failure = 2
        others         = 3.
     r_is_locked = xsdbool( sy-subrc = 0 ).
  endmethod.
  method unlock_table_line.
    call function ‘DEQUEUE_E_TABLEE‘
      exporting
        mode_rstable = lock_types-exclusive    " Lock mode for table RSTABLE
        tabname      = i_tabname    " 01th enqueue argument
        varkey       = i_varkey    " 02th enqueue argument
        _scope       = scope_range-_2.
  endmethod.
  method create_varkey.
    r_varkey = |{ i_sflight_ref->mandt }{ i_sflight_ref->carrid }{ i_sflight_ref->connid }{ i_sflight_ref->fldate }|.
  endmethod.
endclass.

最后,运行例子:

initialization.
data(excel_carrier) = new lcl_excel_carrier( ).
data(rfc_carrier) = new lcl_rfc_carrier( ).
data(database_saver) = new lcl_database_saver( ).

try.
    database_saver->save_data( i_carrier = excel_carrier ).
catch cx_sy_assign_cast_illegal_cast.
catch cx_sy_assign_cast_unknown_type.
catch cx_sy_assign_cast_error.
endtry.

try.
    database_saver->save_data( i_carrier = rfc_carrier ).
catch cx_sy_assign_cast_illegal_cast.
catch cx_sy_assign_cast_unknown_type.
catch cx_sy_assign_cast_error.
endtry.

如你所见,通过把抽象部分移动到接口层面,我们可以确保任何实现了linf_sflight_carrier接口的类可以传输给saver方法并且被正确处理。

另一个该实现的优点是可以快速简单地创建模拟对象来进行单元测试。可测试的代码即更好的代码。

这就是本文的全部内容了,愿你喜欢??。

总结

  • 接口描述了一个会被类承诺提供的功能集。这是一个公共方法集。
  • 接口中不含代码,只有公共方法和数据声明
  • 任何实现了接口的类必须实现接口的承诺。例如,为每个这些公共方法写代码。
  • 一个类也许会实现多个接口,这是ABAP中的多继承。
  • 而子类与父类间的关系是“是”的关系(例如,鸭子是鸟),接口则表达了一种“有”的关系(如“类XYZ有一个用于发送邮件的方法”)。
  • 通用的OO原则是倾向于组合(composition)而非继承(inheritance)。例如,在可能的情况下,不是使用子类而是使用实现接口的方式达到目的。
  • 一个真实世界的例子是,某个人平日里是会计,而业余时间是救火志愿者。消防队只关心他救火的能力,相应地雇主则只在意他的会计能力。
  • 在编程的世界里,类变量可以被定义为接口,然后任何实现该接口的类都可以被传入(需要这个接口的位置)。调用程序只能访问接口的方法,并不知道该类还有进行其它种类行为的能力。

原文地址:https://www.cnblogs.com/hhelibeb/p/8919767.html

时间: 2024-11-05 14:49:10

通过接口标准化ABAP OO开发的相关文章

ABAP OO 开发语法整理

[转自 http://blog.csdn.net/saphome/article/details/6956933] 在类中,只能用TYPE 附加关键字指定数据类型. •TYPES: 一般的类型定义方法 •CONSTANTS: 一般的常数定义方法 只能用LIKE 引用本地数据对象. •DATA: var1 TYPE <TYPES 定义的类型或字典类型>. •DATA: var2 LIKE var1 <不能使用字典对象>. 用DATA 声明的公共属性如果加了READ-ONLY 附加 关

蓝牙(BLE)应用框架接口设计和应用开发——以TI CC2541为例

本文从功能需求的角度分析一般蓝牙BLE单芯片的应用框架(SDK Framework)的接口设计过程,并以TI CC2541为例说明BLE的应用开发方法. 一.应用框架(Framework) 我们熟知的Framework包括Android Framework.Linux QT.Windows MFC.应用框架抽象并封装实现了一般应用场景的需求,完成应用开发的80%,剩下的20%则以回调(callback)和接口的方式供应用开发人员调用以完成具体的需求. 一般Framework完成的工作包括:任务分

was集群下基于接口分布式架构和开发经验谈

某b项目是我首次采用was环境下架构和开发的手机wap应用,尽管做到了该项目的主程,但对此项目的全面构件依然有不清楚的地方,因此在这里我只能简单的谈谈开发中遇到的问题怎么处理和应对办法. 记得第一天接触这个项目时,只记得些案例代码(不知道那些是对的,那些是错的)似曾相识,但不懂如何动手写下第一个helloword,因其中的基于接口开发的ejb的架构以前根本就没接触过.好了,没办法,于是只有硬着头皮去尝试第一个基于接口开发的ejb的第一个查询方法(呵呵最简单了吧).因为一切都是新的,一没有相对完整

[OO]ABAP OO 语法--实例看 &quot;=&quot;与&quot;?=&quot; 的区别[转]

ABAP OO 面向对象语法   实例看  "=" 与 "?=" 的区别 1. "="  同类型赋值*&---------------------------------------------------------------------**& Report  Z_WYS_TYPEREF01*&作者:永上*&----------------------------------------------------

如何通过接口调试音视频开发的内核模式

自AnyChat r4236版本开始,支持通过API接口开启AnyChat内核调试模式,输出调试日志信息到日志文件(BRAnyChatCore.log)中. 早期的版本只支持通过配置文件开启内核调试模式,而对于Web.Android.iOS等不方便使用配置文件的平台则不能开启内核调试模式,新版本使得所有平台均可以开启内核调试模式. 在使用AnyChat SDK开发的过程中,通常遇到问题可以通过分析相应的日志文件(客户端:BRAnyChatCore.log,服务器:AnyChatCoreServe

ABAP OO练习

继续我们的看实例学习abap面向对象编程. 在此次的例子中,我们以雇员的工资为蓝图,设计一个程序,然后再一步一步的改进我们写的程序,使其有更好的扩展性以及维护性. 该例中所使用的基类是employee类,其子类包括:boss类,不管工作多长时间,他总是有固定的周薪(好爽!).销售员类commission worker,他的收入是一小部分工资加上销售额的一定提成.计件工类piece worker,他的收入取决于生产的计件数量. 我会列出代码,基本上是一节一个类,最后是这些类的测试程序. 先给出类图

kepware http接口 c语言(libcrul)开发

列出所有变量 CURL *hnd = curl_easy_init(); curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET"); curl_easy_setopt(hnd, CURLOPT_URL, "http://127.0.0.1:39321/iotgateway/browse"); struct curl_slist *headers = NULL; headers = curl_slist_append(he

[SAP ABAP开发技术总结]业务对象和BAPI

目录导航 声明:原创作品,转载时请注明文章来自SAP师太博客,并以超链接形式标明文章原始出处,否则将追究法律责任!原文出自: 18.3.         业务对象和BAPI 184 18.3.1.     SAP业务对象(SWO1)... 184 18.3.1.1.           业务对象类型的组成... 185 18.3.1.2.           业务对象(BO)设计... 185 18.3.1.2.1.       创建业务表... 185 18.3.1.2.2.       创建

Amzon MWS API开发之订单接口

Amzon MWS API开发之订单接口 Amazon订单接口是Amazon MWS 开发接口中的一大块,我们可以通过接口调用来获得订单数据. 在调用接口之前,首先我们要获得相关店铺商家的店铺密钥等信息.如下: 在此我将所有信息定义在一个类中,采用序列化的方式,方便存取值. 1 /// <summary> 2 /// 账户信息 3 /// </summary> 4 [Serializable] 5 public class AccountConfig : BaseConfig<