透過介面標準化ABAP OO開發

lee_lgw發表於2021-09-09


正文

本文是對介面程式設計的討論,希望能對年輕的開發者有所幫助。

要點:

  • 透過介面對類方法進行更高層的抽象

  • 介面使程式碼清晰易讀

  • 介面使你可以建立模擬物件(Mockup Object)以提高程式碼的可測試性

  • 幫助實現

  • 可以在不使用和型別轉換的前提下使用多種類的不同例項。

因為在學習ABAP之前,我曾經學習過其它面嚮物件語言,因此我很糾結於ABAP中不存在的一個特性——過載方法(overload)。

也許你會問,過載是什麼?

過載就是函式或者方法有相同的名稱,但是引數列表和實現不相同的情形。

沒有了過載,在某種程度上,類也許會變的過大,並且難以追蹤那些有著相似行為但是名字不同的方法。

介面不提供過載能力,但是透過限制名字不同但是功能相近的方法的數量,介面可以整理和簡化你的程式碼。

 

本文連結:http://www.cnblogs.com/hhelibeb/p/8919767.html

英文原文:

簡介

在ABAP中類的繼承是單一繼承(每個類只能有一個父類),介面實現可以有多個。

圖片描述

例如,上圖中的LCL_Child_Class繼承LCL_Parent_Class中所有的非私有變數、方法、型別和常量,並且必須實現LINF_Utility和LINF_Saver介面中所有的功能。

為了解釋介面的定義,我將使用個“不怎麼專業”的描述——它是一個類似於類的實體,不包含所宣告的方法的任何具體實現,但是它可能包含常量、型別和變數。介面無法被初始化。

預設情況下介面的所有方法都必須被實現——這是物件導向程式設計中的一個通常的強制規則。不能允許介面方法的實現變得可選擇,但是這不屬於本文的討論範圍,所以不會展開論述。

(譯註:原文評論指出,在ABAP中,可以使用DEFAULT IGNORE|FAIL附加項指定一個可選的介面方法,雖然好像並沒有什麼用)

“真實”用例

設想下我們有個程式,需要從多種資料來源獲取資料並更新到表SFLIGHT:

  • Excel上傳

  • 上傳

  • 在程式執行期間上傳修改和插入的行

當然我們可以在該清單中新增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 '2',
               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.

圖片描述

在這裡,你也許會問,為什麼我們需要這麼多類來完成一個很簡單的工作?為什麼我們不利用相似的類繼承或者是單個類來實現目的?

答案是顯然的:。如果你想要知道關於它的更多資訊,可以留言回覆,我將建立另一篇部落格單獨講這一話題。

回到主題——接下來是類:

圖片描述

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   = 1
        system_failure = 2
        others         = 3.
     r_is_locked = xsdbool( sy-subrc = 0 ).
  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方法並且被正確處理。

另一個該實現的優點是可以快速簡單地建立模擬物件來進行單元測試。可測試的程式碼即更好的程式碼。

這就是本文的全部內容了,願你喜歡。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1020/viewspace-2800650/,如需轉載,請註明出處,否則將追究法律責任。

相關文章