通过接口标准化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图 ...
随机推荐
- 70后.net老猿,尚能饭否?
程序猿的大限 距离上一次主动找工作,快到5年了,到现在的东家,是差不多3年前猎头挖过来的,而当时东家刚刚被欧洲一家有百年历史的跨国企业集团收购,所以我也就有幸成了一名“外企员工”,但是集团保留原东家人 ...
- 源码解析Flask的配置文件
在flask里,我们常在主文件中定义某些配置,比如: app.debug = True app.secret_key = 'helloworld!!' 实际上,flask中默认可以进行可选的配置项有很 ...
- Mego(03) - ORM框架的新选择
前言 从之前的两遍文章可以看出ORM的现状. Mego(01) - NET中主流ORM框架性能对比 Mego(02) - NET主流ORM框架分析 首先我们先谈下一个我们希望的ORM框架是什么样子的: ...
- Linq GroupBy
//Linq //var result = from p in personList // group p by p.Id // into grouped // select new { Id = g ...
- 新概念英语(1-63)Thank you, doctor.
新概念英语(1-63)Thank you, doctor. Who else is in bed today? why? A:How's Jimmy today? B:Better. Thank yo ...
- global文件中的application_start方法中做: 定时器
<%@ Application Language="C#" %> <%@ import Namespace="System.Data" %&g ...
- 模板引擎Jade详解
有用的符号: | 竖杠后的字符会被原样输出 · 点表示下一级的所有字符都会被原样输出,不再被识别.(就是|的升级版,实现批量) include 表示引用外部文件 短杠说明后面跟着的字符只是一段代码(与 ...
- flask 视图函数的使用
flask框架 视图函数当中 各种实用情况简单配置 1 建立连接 2 路由参数 3 返回网络状态码 4 自定义错误页面 5 重定向 6 正则url限制 和 url 优化 7 设置和获取cookie # ...
- python、java实现二叉树,细说二叉树添加节点、深度优先(先序、中序、后续)遍历 、广度优先 遍历算法
数据结构可以说是编程的内功心法,掌握好数据结构真的非常重要.目前基本上流行的数据结构都是c和c++版本的,我最近在学习python,尝试着用python实现了二叉树的基本操作.写下一篇博文,总结一下, ...
- HTML5示例之WebSocket
Web应用程序通常有一些耗时的操作,但有些操作耗时不是很长,一分钟之内能完成.如果采用后台任务队列去异步处理,这样的用户不能实时看到后台处理的情况.倘若用户触发操作后,Web页面能够实时看到后台处理的 ...