Abap 我如何有效地建模在特定点上略有不同的共享代码段?
我正在编写一个用于许多场景(供应商、客户、成本中心、REFX合同等)的数据导出应用程序。 最后有两种主要的导出方式:保存到文件或调用webservice 所以我的想法是创建一个接口Abap 我如何有效地建模在特定点上略有不同的共享代码段?,abap,Abap,我正在编写一个用于许多场景(供应商、客户、成本中心、REFX合同等)的数据导出应用程序。 最后有两种主要的导出方式:保存到文件或调用webservice 所以我的想法是创建一个接口,如果导出,为每个场景实现一个类。 问题是,调用webservice代码在实际调用时略有不同:被调用的方法每次都有不同的名称 到目前为止,我的想法是: 抽象cl\u webservice\u export,为每个场景提供子类。覆盖包含实际调用的方法 cl\u webservice\u导出,成员类型为if\u webse
,如果导出
,为每个场景实现一个类。
问题是,调用webservice代码在实际调用时略有不同:被调用的方法每次都有不同的名称
到目前为止,我的想法是:
cl\u webservice\u export
,为每个场景提供子类。覆盖包含实际调用的方法cl\u webservice\u导出
,成员类型为if\u webservice\u调用
。为实现if\u webservice\u call
methodcall\u webservice()
调用方法webservice\u实例->(方法\u名称)
内部
包含实际调用并将(方法名称)
传递到cl\u webservice\u导出
的具体cl\u webservice\u导出方法export\u via\u webservice
是由cl\u webservice\u export
或通过提供的公共接口,如果\u export
METHODS export_via_webservice
IMPORTING
VALUE(it_xml_strings) TYPE tt_xml_string_table
io_service_consumer TYPE REF TO ztnco_service_vmsoap
RETURNING
VALUE(rt_export_results) TYPE tt_xml_string_table.
METHOD export_via_webservice.
LOOP AT it_xml_strings INTO DATA(lv_xml_string).
call_webservice(
EXPORTING
io_service = io_service_consumer
iv_xml_string = lv_xml_string-xmlstring
RECEIVING
rv_result = DATA(lv_result)
).
rt_export_results = VALUE #( BASE rt_export_results (
lifnr = lv_xml_string-xmlstring
xmlstring = lv_result ) ).
ENDLOOP.
ENDMETHOD.
实际的Web服务调用,由if\u webservice\u call覆盖或提供
METHODS call_webservice
IMPORTING
io_service TYPE REF TO ztnco_service_vmsoap
iv_xml_string TYPE string
RETURNING
VALUE(rv_result) TYPE string.
METHOD call_webservice.
TRY.
io_service->import_creditor(
EXPORTING
input = VALUE #( xml_creditor_data = iv_xml_string )
IMPORTING
output = DATA(lv_output)
).
CATCH cx_ai_system_fault INTO DATA(lx_exception).
ENDTRY.
rv_result = lv_output-import_creditor_result.
ENDMETHOD.
你将如何解决这个问题,也许还有其他更好的方法吗?我知道解决这个问题的三种常见模式。它们按质量的升序排列:
单个实现
创建一个接口if\u export
,以及一个为您需要的每个web服务导出变体实现它的类,即cl\u webservice\u export\u variant\u a
,cl\u webservice\u export\u variant\u b
,等等
主要的优点是直观的简单类设计和实现的完全独立性,避免了从一个变量到另一个变量的意外溢出
主要的缺点是,如果不同变体的代码只在很少的、次要的位置上发生变化,那么它们之间可能存在大量的代码重复
您已经将其绘制为选项2,并且已经强调它是场景中最不理想的解决方案。代码复制永远不受欢迎。更重要的是,在某些方法名称中,您的web服务调用只是略有不同
总之,这种模式相当糟糕,您不应该主动选择它。它通常是独立存在的,当人们从变体a开始,几个月后通过复制粘贴现有类添加变体b,然后忘记重构代码以消除重复部分
战略模式
这种设计通常被称为。创建一个接口if\u export
,以及一个abstract
类cl\u abstract\u webservice\u export
,该接口实现该接口并包含大部分web服务调用代码
除了这个细节:应该调用的方法的名称不是硬编码的,而是通过调用protected
子方法get\u service\u name
来检索的。抽象类未实现此方法。相反,您创建抽象类的子类,即cl\u concrete\u webservice\u export\u variant\u a
,cl\u concrete\u webservice\u export\u variant\u b
,等等。这些类只实现继承的受保护方法get\u service\u name
,提供它们的具体需求
主要的优点是,这种模式完全避免了代码重复,可以进一步扩展,并且已经成功地应用于许多框架实现中
主要的缺点是,当第一个变量到达时,模式开始侵蚀,而第一个变量并不完全适合,例如,因为它不仅改变了方法名称,还改变了一些参数。然后,进化需要对所有涉及的类进行深入的重新设计,这可能会带来相当大的成本。另一个缺点是继承设置会使编写单元测试变得很麻烦:例如,抽象类的单元测试需要组成一个测试,该测试需要将抽象类划分为子类,并使用感知和模拟代码覆盖受保护的方法——所有这些都是可能的,但不像类之间的接口那样整洁
您已经将其作为选项1绘制。总之,如果您可以控制所有涉及的类,并且愿意花费一些额外的精力来保持模式干净,以防它不完全适合,那么我建议您选择这种模式
作文
组合意味着避免继承,以利于独立类之间的松散交互。创建接口if\u export
,并将其具体实现为cl\u webservice\u export\u variant\u a
,cl\u webservice\u export\u variant\u b
等
将共享代码移出到类cl\u export\u webservice\u调用者
,该调用者接收所需的任何数据和变量(例如方法名)。让变量类调用此共享代码。要完成类设计,请引入另一个接口if\u export\u webservice\u caller
,该接口将变体类与caller类解耦
主要的优点是所有类都是相互独立的,并且可以以几种不同的方式进行重组。例如,如果将来您需要引入一个变体X,它将以完全不同的方式调用其web服务,那么您可以简单地添加它,而无需重新设计任何其他涉及的类。与策略模式相反,为所有涉及的类编写单元测试非常简单
这种模式没有真正的缺点。(它需要多个接口这一表面上的缺点并不是真正的一个-面向对象的目的是明确地分离关注点,而不是最小化