简体   繁体   English

在ABAP中对类方法的依赖注入

[英]Dependency injection to class-method in ABAP

I have a scenario where my class method (A) calls another class method (B). 我有一个方案,其中我的类方法(A)调用了另一个类方法(B)。 So A is depended on B. I'd like to get rid of the dependency to be able to run unit tests. 因此,A依赖于B。我想摆脱依赖关系,以便能够运行单元测试。 Decoupling and dependency injection are somewhat based on instantiation. 解耦和依赖注入在某种程度上基于实例。 But class methods (static methods) don't need an instance by nature. 但是类方法(静态方法)本质上不需要实例。 I've already made two solutions working, but none of them seems 100% for me: 我已经使两种解决方案都能正常工作,但是对我来说,它们似乎都不是100%:

  1. We create instance of class B (production code) or instance of the test double (for unit test). 我们创建类B的实例(生产代码)或测试double的实例(用于单元测试)。 We inject it as a parameter to the class method under test. 我们将其作为参数输入到被测类方法中。 The inner class method is called on the injected instance, not on the class. 内部类方法在注入的实例上调用,而不是在类上调用。

I don't like we need to make an instance of class, although we are using class method. 我不喜欢我们需要创建一个类的实例,尽管我们使用的是类方法。 It can take time. 这可能需要一些时间。 It needs a bit more code. 它需要更多的代码。

  1. We inject actual class name as a string parameter and we use dynamic CALL METHOD . 我们将实际的类名作为字符串参数注入,并使用动态CALL METHOD

As I am not fan of interpreted languages, I consider this a mess that can bring serious runtime problems. 由于我不喜欢解释性语言,因此我认为这很混乱,可能带来严重的运行时问题。 Since we do this to implement unit tests and consequently to eliminate possible bugs; 由于我们这样做是为了实施单元测试,因此消除了可能的错误; using dynamic calls seems counterproductive. 使用动态调用似乎适得其反。 Also it is painful to work with parameters. 使用参数也很痛苦。

Is there another way to solve this? 还有另一种解决方法吗? Is there some important point I missed? 我错过了一些重要的观点吗?

Bellow, there are key parts of both solutions. 在下面,这两种解决方案都有关键部分。 There are not essential to understand the problem, but they might help. 理解问题不是必需的,但是它们可能会有所帮助。

1) 1)

INTERFACE lif_readfile.
  CLASS-METHODS gui_upload
    IMPORTING file_path TYPE string
    CHANGING data_tab  TYPE truxs_t_text_data.
ENDINTERFACE.

CLASS lcl_file_operations DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      get_file_length
        IMPORTING
          !file_path         TYPE string
        CHANGING
          !filereader        TYPE REF TO lif_readfile OPTIONAL
        RETURNING
          VALUE(text_length) TYPE i.
ENDCLASS.

CLASS lcl_file_operations IMPLEMENTATION.
  METHOD get_file_length.

*create instance of default productive class
    IF filereader IS NOT BOUND.
      filereader = NEW lcl_readfile( ).
    ENDIF.

*use instance to call class method
    filereader->gui_upload(
      EXPORTING file_path = file_path
        CHANGING data_tab = lt_data
    ).

*code under test here..

  ENDMETHOD.
ENDCLASS.

2) 2)

CLASS lcl_file_operations DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      get_file_length
        IMPORTING
          !file_path         TYPE string
          !classname         TYPE string DEFAULT 'LCL_READFILE'
        RETURNING
          VALUE(text_length) TYPE i.
ENDCLASS.

CLASS lcl_file_operations IMPLEMENTATION.
  METHOD get_file_length.

*parameter definition
    ptab = VALUE #( ( name  = 'FILE_PATH'
                      kind  = cl_abap_objectdescr=>exporting
                      value = REF #( file_path ) )
                    ( name  = 'DATA_TAB'
                      kind  = cl_abap_objectdescr=>changing
                      value = REF #( lt_data ) ) ).

    DATA(meth)     = 'LIF_READFILE~GUI_UPLOAD'.

*dynamic call
    CALL METHOD (classname)=>(meth) PARAMETER-TABLE ptab.

*code under test here..

  ENDMETHOD.
ENDCLASS.

Your question is language-agnostic I think. 我认为您的问题与语言无关。 Did you check the multiple answers which have been provided in the forum, and what do you think of the different approaches proposed? 您是否检查了论坛中提供的多个答案,您对提出的不同方法有何看法?

You may wrap the static call inside new interface and classes with an instance method which maps to the static method of the right class, statically specified. 您可以使用实例方法将静态调用包装在新接口和类中,该实例方法映射到静态指定的正确类的静态方法。

The solution specific to ABAP about using the "class name" as a variable, as you have shown, I personally don't like it, but I think it's just a matter of individual preference, not really important. 正如您所显示的,ABAP特有的关于使用“类名”作为变量的解决方案,我个人不喜欢它,但是我认为这只是个人喜好,并不重要。 PS: the term "interpreted language" is for a programming language, the opposite (to be simplistic) is "compiled language"; PS:术语“解释语言”是一种编程语言,与之相反(为简单起见)是“编译语言”; what you are talking about is more about a dynamic link . 您正在谈论的更多是关于动态链接

I've found so far two solutions that appears better to me. 到目前为止,我已经找到了两种对我来说更好的解决方案。 But if you know even a better solution; 但是,如果您知道更好的解决方案, I am still looking forward to try it. 我仍然期待尝试。

Factory+injector (dependency lookup) Factory + Injector(依赖查找)

Improved solution 1 – handling of instances is moved to the factory. 改进的解决方案1 ​​–实例处理已移至工厂。 Code for the factory and injector is not provided – it is standard solution. 未提供工厂代码和喷油器代码–这是标准解决方案。

CLASS lcl_file_operations DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      get_file_length
        IMPORTING
          !file_path         TYPE string
        RETURNING
          VALUE(text_length) TYPE i.
ENDCLASS.


CLASS lcl_file_operations IMPLEMENTATION.
  METHOD get_file_length.

    myfactory=>get_filereader( )->gui_upload(
      EXPORTING file_path = file_path
        CHANGING data_tab = lt_data
    ).

*code under test here..

  ENDMETHOD.
ENDCLASS.

Pros 优点

  • Cleaner and shorter code under test. 正在测试的代码更简洁,更短。 Instance is created and tested somewhere else. 实例是在其他地方创建和测试的。

Cons 缺点

  • It still creates instances. 它仍然创建实例。
  • More code in total. 总共有更多代码。

Using TEST-SEAM and TEST-INJECTION 使用TEST-SEAM和TEST-INJECTION

CLASS zcl_file_operations IMPLEMENTATION.
  METHOD get_file_length.

   TEST-SEAM seam_gui_upload.
     zcl_filereader=>gui_upload(
        EXPORTING file_path = file_path
        CHANGING data_tab = lt_data
     ).
   END-TEST-SEAM.

*code under test here..

  ENDMETHOD.
ENDCLASS.

FOR TESTING method 测试方法

*...
TEST-INJECTION seam_gui_upload.
 ztc_testdouble=>gui_upload(
    EXPORTING file_path = file_path
    CHANGING data_tab = lt_data
 ).
END-TEST-INJECTION.
*...

Pros 优点

  • This seems like the best solution so far. 到目前为止,这似乎是最好的解决方案。
  • Using class-methods; 使用类方法; no instances created. 没有创建实例。
  • Shortest code in total. 总共最短的代码。

Cons 缺点

  • Considered dirty technique by community (recommended only for legacy code). 被社区认为是肮脏的技术(仅建议用于遗留代码)。
  • Slight pollution of code under test. 被测代码受到轻微污染。

Note 注意

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM