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

要点:

  • 通过接口对类方法进行更高层的抽象
  • 接口使代码清晰易读
  • 接口使你可以创建模拟对象(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 '',
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 =
system_failure =
others = .
r_is_locked = xsdbool( sy-subrc = ).
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 OO开发的更多相关文章

  1. ABAP OO 开发语法整理

    [转自 http://blog.csdn.net/saphome/article/details/6956933] 在类中,只能用TYPE 附加关键字指定数据类型. •TYPES: 一般的类型定义方法 ...

  2. ABAP OO的八大理由

    原贴地址:http://scnblogs.techweb.com.cn/abaplv/archives/127.html 几年前SAP BASIS 4.6为ABAP扩展了OO功能,这是很多传统的ABA ...

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

    本文从功能需求的角度分析一般蓝牙BLE单芯片的应用框架(SDK Framework)的接口设计过程,并以TI CC2541为例说明BLE的应用开发方法. 一.应用框架(Framework) 我们熟知的 ...

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

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/luozhonghua2014/article/details/34084935    某b项目是我首 ...

  5. 第一节:从面向对象思想(oo)开发、接口、抽象类以及二者比较

    一. 面向对象思想 1. 面向过程(OP)和面向对象(OO)的区别: (1):面向过程就是排着用最简单的代码一步一步写下去,没有封装,当业务复杂的时候,改动就很麻烦了 (2):面向对象将复杂的业务分离 ...

  6. 引入OO开发报表后的感想

    很早就想尝试着在常规的报表开发中 引入OO了 趁着程序略复杂 时间略充裕 终于尝试了一把-咩哈哈~~ 以下来自我的evernote笔记 有点语无伦次-忍忍~~ -------------------- ...

  7. OO开发思想:面向对象的开发方法(Object oriented,OO)

    面向对象的开发方法(Object oriented,OO)认为是好文章吧,拿来分享一下(转载) 面向对象的开发方法(Object oriented,OO) 从事软件开发的工程 师们常常有这样 的体会: ...

  8. ABAP OO与ALV结合方式探索(2)

    接上篇 一开始设计的BO 类是为了实现功能而实现功能 从类的单一职责的角度而言 先把这个BO对象拆分   这里又有一个需要考虑的点:   如何传递内表数据到ALV 如果引入一个中间变量,数据就会被do ...

  9. 快递Api接口 & 微信公众号开发流程

    之前的文章,已经分析过快递Api接口可能被使用的需求及场景:今天呢,简单给大家介绍一下微信公众号中怎么来使用快递Api接口,来完成我们的需求和业务场景. 开发语言:Nodejs,其中用到了Neo4j图 ...

随机推荐

  1. Mybatis-select-返回值类型错误理解

    Mybatis :Cause: java.lang.UnsupportedOperationException异常: 今天在写一个练手项目,作为初学Mybatis的小白,想着这里findByEmp_i ...

  2. 将Python程序打包为exe方法

    将Python程序打包为exe文件,需要使用到的工具是pyinstaller pyinstaller是一个多平台的Python程序打包为exe的免费工具 安装pyinstaller: 1,在Windo ...

  3. Mego开发文档 - 数据库建模

    数据库建模 我们还提供了一些其他的特性,用于定制化数据库对应的数据结构. 表映射 框架默认会使用CLR类型名称做为实际数据库的表名,当两者不一致时可以使用该特性强制表名称. [Table(" ...

  4. 新概念英语(1-69)The car race

    新概念英语(1-69)The car race Which car was the winner in 1995 ? There is  car race near our town every ye ...

  5. python入门(4)第一个python程序

    python入门(4)第一个python程序 在交互式环境的提示符>>>下,直接输入代码,按回车,就可以立刻得到代码执行结果.现在,试试输入100+200,看看计算结果是不是300: ...

  6. matlab 对tif数据高程图的处理分析

    temp=z(101:2200,101:2200) 根据图像属性可得此为2300*2300的tif图像,由于需要将其划分为9宫格,所以begin点设置为101,end点设置为2200,temp转化为可 ...

  7. Python学习之条件判断和循环

    #coding= utf-8 # 条件判断和循环 # 如果if语句判断是True,就把缩进的两行print语句执行了,否则,什么也不做 age1 = 20 if age1 >= 18: prin ...

  8. NOPI实现导入导出泛型List,支持自定义列

    概述 业务上需要自定义列的Excel的导入导出,在网上看了好多资料,很多都是有Bug而且都是支持Excel和DataTable的转换,所以自己总结了一下,应用.NET平台上的NPOI封装了支持自定义列 ...

  9. R数据分析 第一篇:温习概率论

    概率论是人们在长期实践中发现的理论,是客观存在的.自然界和社会上发生的现象是多种多样的,有一类现象,在一定条件下必然发生,称作确定性现象,而概率论研究的现象是不确定性现象,嗯嗯,醒醒,概率论研究的对象 ...

  10. [洛谷P1198/BZOJ1012][JSOI2008] 最大数 - 树状数组/线段树?

    其实已经学了树状数组和线段树,然而懒得做题,所以至今没写多少博客 Description 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数 ...