Abap 我如何有效地建模在特定点上略有不同的共享代码段?

Abap 我如何有效地建模在特定点上略有不同的共享代码段?,abap,Abap,我正在编写一个用于许多场景(供应商、客户、成本中心、REFX合同等)的数据导出应用程序。 最后有两种主要的导出方式:保存到文件或调用webservice 所以我的想法是创建一个接口,如果导出,为每个场景实现一个类。 问题是,调用webservice代码在实际调用时略有不同:被调用的方法每次都有不同的名称 到目前为止,我的想法是: 抽象cl\u webservice\u export,为每个场景提供子类。覆盖包含实际调用的方法 cl\u webservice\u导出,成员类型为if\u webse

我正在编写一个用于许多场景(供应商、客户、成本中心、REFX合同等)的数据导出应用程序。 最后有两种主要的导出方式:保存到文件或调用webservice

所以我的想法是创建一个接口
,如果导出
,为每个场景实现一个类。 问题是,调用webservice代码在实际调用时略有不同:被调用的方法每次都有不同的名称

到目前为止,我的想法是:

  • 抽象
    cl\u webservice\u export
    ,为每个场景提供子类。覆盖包含实际调用的方法
  • cl\u webservice\u导出
    ,成员类型为
    if\u webservice\u调用
    。为实现
    if\u webservice\u call
    method
    call\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服务,那么您可以简单地添加它,而无需重新设计任何其他涉及的类。与策略模式相反,为所有涉及的类编写单元测试非常简单

    这种模式没有真正的缺点。(它需要多个接口这一表面上的缺点并不是真正的一个-面向对象的目的是明确地分离关注点,而不是最小化