通过接口标准化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 '',
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开发的更多相关文章
- ABAP OO 开发语法整理
[转自 http://blog.csdn.net/saphome/article/details/6956933] 在类中,只能用TYPE 附加关键字指定数据类型. •TYPES: 一般的类型定义方法 ...
- ABAP OO的八大理由
原贴地址:http://scnblogs.techweb.com.cn/abaplv/archives/127.html 几年前SAP BASIS 4.6为ABAP扩展了OO功能,这是很多传统的ABA ...
- 蓝牙(BLE)应用框架接口设计和应用开发——以TI CC2541为例
本文从功能需求的角度分析一般蓝牙BLE单芯片的应用框架(SDK Framework)的接口设计过程,并以TI CC2541为例说明BLE的应用开发方法. 一.应用框架(Framework) 我们熟知的 ...
- was集群下基于接口分布式架构和开发经验谈
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/luozhonghua2014/article/details/34084935 某b项目是我首 ...
- 第一节:从面向对象思想(oo)开发、接口、抽象类以及二者比较
一. 面向对象思想 1. 面向过程(OP)和面向对象(OO)的区别: (1):面向过程就是排着用最简单的代码一步一步写下去,没有封装,当业务复杂的时候,改动就很麻烦了 (2):面向对象将复杂的业务分离 ...
- 引入OO开发报表后的感想
很早就想尝试着在常规的报表开发中 引入OO了 趁着程序略复杂 时间略充裕 终于尝试了一把-咩哈哈~~ 以下来自我的evernote笔记 有点语无伦次-忍忍~~ -------------------- ...
- OO开发思想:面向对象的开发方法(Object oriented,OO)
面向对象的开发方法(Object oriented,OO)认为是好文章吧,拿来分享一下(转载) 面向对象的开发方法(Object oriented,OO) 从事软件开发的工程 师们常常有这样 的体会: ...
- ABAP OO与ALV结合方式探索(2)
接上篇 一开始设计的BO 类是为了实现功能而实现功能 从类的单一职责的角度而言 先把这个BO对象拆分 这里又有一个需要考虑的点: 如何传递内表数据到ALV 如果引入一个中间变量,数据就会被do ...
- 快递Api接口 & 微信公众号开发流程
之前的文章,已经分析过快递Api接口可能被使用的需求及场景:今天呢,简单给大家介绍一下微信公众号中怎么来使用快递Api接口,来完成我们的需求和业务场景. 开发语言:Nodejs,其中用到了Neo4j图 ...
随机推荐
- 在Vim按了ctrl+s后
在windows我们码代码的时候习惯ctrl+s保存: 但在vim中使用ctrl+s之后终端就没反应了... vim: ctrl+s终止屏幕输出,敲的东西都有效,就是看不见. ctrl+q恢复:
- Linux--初次体验
关于Linux已经听闻很久的大名了,但是一直没有机会来使用,这次趁着放假的机会,来体验一把Linux吧. 之前使用visuabox和Ubuntu16,但是虚拟机总是不能连接互联网,在虚拟机上面无法上网 ...
- Python内置函数(3)——max
英文文档: max(iterable, *[, key, default]) max(arg1, arg2, *args[, key]) Return the largest item in an i ...
- restful架构风格设计准则(四)资源表示和资源访问
读书笔记,原文链接:http://www.cnblogs.com/loveis715/p/4669091.html,感谢作者! 一.资源表示 1.资源表示:使用 单数 vs. 复数 如果一个URL所对 ...
- RxJava系列4(过滤操作符)
RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...
- PV 动态供给 - 每天5分钟玩转 Docker 容器技术(153)
前面的例子中,我们提前创建了 PV,然后通过 PVC 申请 PV 并在 Pod 中使用,这种方式叫做静态供给(Static Provision). 与之对应的是动态供给(Dynamical Provi ...
- 关于ZK框架的onScroll事件的问题
由于我现在所在的公司用到的zk框架,遇到了一个需求frozen on top. 简单来说就是滚动超过范围后,希望有一块东西停留在滚动窗口的顶部. 一.zk框架 查看了zk的8.x版本,发现组件的支持的 ...
- Linux:日期用法,及格式定义
在shell脚本中经常会需要获取当前日期的地方,linux的系统时间在shell里是可以直接调用系统变量: 获取今天时期---`date +%Y%m%d` 或 `date +%F` 或 $(date ...
- 使用net.sf.cssbox实现网页截图
需要引用包,在pom.xml中添加引用: <dependency> <groupId>net.sf.cssbox</groupId> <artifactId& ...
- POJ-1753 Flip Game---二进制枚举子集
题目链接: https://vjudge.net/problem/POJ-1753 题目大意: 有4*4的正方形,每个格子要么是黑色,要么是白色,当把一个格子的颜色改变(黑->白或者白-> ...