天天看點

我的BRF+自學教程(三):動态技術

開發者們可以在程式設計中使用各種動态技術,比如RTTS,比如通過動态的類建立和多态來實作功能的平滑擴充。BRF+開發中也存在一些動态手段。本文将介紹3種不同場景下的動态實踐方式。其中第一種是純配置的,第二和第三種是程式設計相關的。

本文連結:https://www.cnblogs.com/hhelibeb/p/9571707.html

我的BRF+教程系列:https://www.cnblogs.com/hhelibeb/tag/BRFplus/

1,動态表達式(Dynamic Expression)

動态表達式是一種特殊類型的表達式,它可以用來實作對其它表達式的動态調用。動态表達式的應用場景之一是,有很多個表達式可以供使用者選擇,這些表達式使用基本一樣的上下文對象,你不希望為每個表達式建立一個函數(因為建立它們的過程太繁瑣),而是希望在一個統一的入口函數去調用。

1)選項

  • 被調用的表達式(Called Expression):選擇一個表達式,将它配置設定給動态表達式。被配置設定的表達式會在運作期間被動态表達式調用。注意,雖然被稱為被調用的表達式,但是它并不是被動态調用的表達式,它的傳回結果才是被動态調用的表達式的ID。Called Expression可以處理動态表達式的上下文,結果中必須包含一個表達式的ID,該表達式的結果會被傳回給動态表達式。
  • 結果資料對象(Result Data Object:)結果資料對象可以使用任何種類的對象。然而,很重要的一點是確定第二個被調用的表達式的結果資料對象和動态表達式的結果資料對象是相容的。

2)限制

動态表達式可以提供靈活性,但它也有缺陷,因為動态表達式類型的特質,使得某些大部分可以應用在其它BRF+表達式上的檢查無法應用在動态表達式上面。是以這可能導緻一些設計缺陷無法被檢查出來,隻有在運作期間才會暴露。此外,動态表達式是不支援代碼生成模式的,隻能使用解釋模式,這使得它的性能不太好,在對時間要求高或資料負載量大的時候不建議使用它。

3)例子

實際操作部分比較簡單,讀完上面的文字之後應該可以很順利地出來,這裡就不再一圖一圖地貼了。可以參考官方文檔。

2,動态建立決策表(Decision Table)

通過使用ABAP代碼,可以動态地建立BRFplus中的decisiong table和function等對象。

(本節的内容基本來自于Create decision table & it’s entries dynamically in BRF + Workbench through API)

1)前提

要讀懂本節,需要兩方面的知識,

  1. 基本的ABAP面向對象知識。
  2. 基本的BRF+知識。

2)需求

通過一個簡單的report程式來建立決策表和它的條目。

3)使用的接口

  1. IF_FDT_FACTORY
  2. IF_FDT_DECISION_TABLE
  3. IF_FDT_FUNCTION

4)建立應用

在工作台中建立一個應用,接下來将會在這個應用中動态地建立決策表。

我的BRF+自學教程(三):動态技術

5)複制代碼

把以下代碼粘貼到自定義程式中,代碼的具體意義寫在注釋中,注意ID要替換成你自己建立的BRF+應用的ID

DATA: lo_factory        TYPE REF TO if_fdt_factory,
      lt_message        TYPE if_fdt_types=>t_message,
      lv_message        TYPE string,
      lv_boolean        TYPE abap_bool,
      lo_element        TYPE REF TO if_fdt_element,
      lo_table          TYPE REF TO if_fdt_table,
      lo_structure      TYPE REF TO if_fdt_structure,
      lv_element1_id    TYPE if_fdt_types=>id,
      lv_element2_id    TYPE if_fdt_types=>id,
      lv_element3_id    TYPE if_fdt_types=>id,
      lv_structure_id   TYPE if_fdt_types=>id,
      lo_constant       TYPE REF TO if_fdt_constant,
      ls_element        TYPE if_fdt_structure=>s_element,
      lts_element       TYPE if_fdt_structure=>ts_element,
      lv_string         TYPE string,
      ls_range          TYPE if_fdt_decision_table=>s_range,
      ls_table_data     TYPE if_fdt_decision_table=>s_table_data,
      lo_decision_table TYPE REF TO if_fdt_decision_table,
      lo_function       TYPE REF TO if_fdt_function,
      lo_context        TYPE REF TO if_fdt_context,
      lts_context_id    TYPE if_fdt_types=>ts_object_id,
      lts_table_data    TYPE if_fdt_decision_table=>ts_table_data,
      lts_column        TYPE if_fdt_decision_table=>ts_column,
      lv_actv_failed    TYPE abap_bool,
      lx_fdt            TYPE REF TO cx_fdt,
      lv_dt_id          TYPE if_fdt_types=>id,
      ls_column         LIKE LINE OF lts_column.

FIELD-SYMBOLS: <ls_message> TYPE if_fdt_types=>s_message,

               <lv_value>   TYPE any.

* 擷取FDT工廠執行個體,使用上面建立的應用ID
lo_factory = cl_fdt_factory=>if_fdt_factory~get_instance(
                           '005056A4CCA61ED8AA9AAF84A7712616' ).


*建立資料對象
lo_element ?= lo_factory->get_data_object(
     iv_data_object_type = if_fdt_constants=>gc_data_object_type_element ).

lo_element->if_fdt_transaction~enqueue( ).

lo_element->if_fdt_admin_data~set_name( 'IV_VAR1' ).

lo_element->set_element_type( if_fdt_constants=>gc_element_type_text ).

lo_element->if_fdt_transaction~activate(
   IMPORTING
     et_message           = lt_message
     ev_activation_failed = lv_boolean ).

IF lv_boolean EQ abap_true.

*如果激活失敗,需要處理

  lo_element->if_fdt_transaction~dequeue( ).

ELSE.

  lo_element->if_fdt_transaction~save( ).

  lo_element->if_fdt_transaction~dequeue( ).

* 通常需要把ID單獨存下來,以便後續操作
  lv_element1_id = lo_element->mv_id.

  ls_element-position = 1.

  ls_element-element_id = lv_element1_id.

  APPEND ls_element TO lts_element.

ENDIF.

INSERT lv_element1_id INTO TABLE lts_context_id.

*建立另一個元素

lo_element ?= lo_factory->get_data_object(
     iv_data_object_type = if_fdt_constants=>gc_data_object_type_element ).

lo_element->if_fdt_transaction~enqueue( ).

lo_element->if_fdt_admin_data~set_name( 'IV_VAR2' ).

* 設定元素類型(可以搜尋if_fdt_constants=>gc_element_type_* 得到可用元素類型清單

lo_element->set_element_type( if_fdt_constants=>gc_element_type_text ).

lo_element->if_fdt_transaction~activate(
   IMPORTING
     et_message           = lt_message
     ev_activation_failed = lv_boolean ).

IF lv_boolean EQ abap_true.

  lo_element->if_fdt_transaction~dequeue( ).

ELSE.

  lo_element->if_fdt_transaction~save( ).

  lo_element->if_fdt_transaction~dequeue( ).

  lv_element2_id = lo_element->mv_id.

  ls_element-position = 2.

  ls_element-element_id = lv_element2_id.

  APPEND ls_element TO lts_element.

ENDIF.

INSERT lv_element2_id INTO TABLE lts_context_id.

* 建立結果資料元素

lo_element ?= lo_factory->get_data_object(
     iv_data_object_type = if_fdt_constants=>gc_data_object_type_element ).

lo_element->if_fdt_transaction~enqueue( ).

lo_element->if_fdt_admin_data~set_name( 'EV_RESULT' ).

lo_element->set_element_type( if_fdt_constants=>gc_element_type_text ).

lo_element->if_fdt_transaction~activate(
   IMPORTING
     et_message           = lt_message
     ev_activation_failed = lv_boolean ).

IF lv_boolean EQ abap_true.

  lo_element->if_fdt_transaction~dequeue( ).

ELSE.

  lo_element->if_fdt_transaction~save( ).

  lo_element->if_fdt_transaction~dequeue( ).

  lv_element3_id = lo_element->mv_id.

  ls_element-position = 3.

  ls_element-element_id = lv_element3_id.

  APPEND ls_element TO lts_element.

ENDIF.

INSERT lv_element3_id INTO TABLE lts_context_id.

* 填充第1列元素
ls_column-col_no     = 1.

ls_column-object_id =   lv_element1_id.

ls_column-is_result  = abap_false.

INSERT ls_column INTO TABLE lts_column.

* 填充第2列元素      
ls_column-col_no = 2. 
 ls_column-object_id = lv_element2_id. 
 ls_column-is_result = abap_false. 
 
INSERT ls_column INTO TABLE lts_column. 

* 填充結果列元素  
ls_column-col_no = 3. 
ls_column-object_id = lv_element3_id. 
ls_column-is_result = abap_true. 

INSERT ls_column INTO TABLE lts_column. 

* 建立并設定決策表表達式
lo_decision_table ?= lo_factory->get_expression( iv_expression_type_id = if_fdt_constants=>gc_exty_decision_table ). 

* 對表達式加鎖.  
lo_decision_table->if_fdt_transaction~enqueue( abap_true ).
* 設定表列  
lo_decision_table->set_columns( its_column = lts_column ). 
lo_decision_table->if_fdt_admin_data~set_name( 'DT_TEST' ). "user defined name. DT_TEST is the decision table Name 

* 使用工廠對象建立一個函數執行個體. 
 lo_function ?= lo_factory->get_function( ). 

* 對函數加鎖.  
lo_function->if_fdt_transaction~enqueue( ). 

* 設定函數上下文對象.  
lo_function->set_context_data_objects( lts_context_id ). 
lo_function->if_fdt_admin_data~set_name( 'FN_TEST' ). "自定義函數名

* 設定函數根表達式.  
lo_function->set_expression( lo_decision_table->mv_id ). 

* 設定單元格(1,1)的條件
ls_table_data-row_no = 1. 
ls_table_data-col_no = 1. 
ls_range-position = 1. 
ls_range-sign = if_fdt_range=>gc_sign_include. 
ls_range-option = if_fdt_range=>gc_option_equal.
CREATE DATA ls_range-r_low_value TYPE if_fdt_types=>element_text. 

ASSIGN ls_range-r_low_value->* TO <lv_value>. <lv_value> = 'MOURI'. 

INSERT ls_range INTO TABLE ls_table_data-ts_range. 

INSERT ls_table_data INTO TABLE lts_table_data. 

CLEAR ls_table_data. . 

*設定單元格(1,2)的條件
ls_table_data-row_no = 1. 
ls_table_data-col_no = 2. 
ls_range-position = 1. 
ls_range-sign = if_fdt_range=>gc_sign_include. 
ls_range-option = if_fdt_range=>gc_option_equal. 

CREATE DATA ls_range-r_low_value TYPE if_fdt_types=>element_text. 

ASSIGN ls_range-r_low_value->* TO <lv_value>. <lv_value> = 'TECH'. 

INSERT ls_range INTO TABLE ls_table_data-ts_range. 

INSERT ls_table_data INTO TABLE lts_table_data. 

CLEAR ls_table_data. **在單元格(1, 3)得到結果  
ls_table_data-row_no = 1. ls_table_data-col_no = 3. 

CREATE DATA ls_table_data-r_value TYPE if_fdt_types=>element_text. 

ASSIGN ls_table_data-r_value->* TO <lv_value>. <lv_value> = 'MOURITECH'. 

INSERT ls_table_data INTO TABLE lts_table_data. 

CLEAR ls_table_data. 

* 設定完全的表資料.  
lo_decision_table->set_table_data( its_data = lts_table_data ). 

* 儲存并激活.  
lo_function->if_fdt_transaction~activate( 
  EXPORTING iv_deep         = abap_true 
  IMPORTING et_message       = lt_message 
        ev_activation_failed = lv_actv_failed ). 

* 如果成功,先儲存對象。無論成功失敗,釋放全部鎖

IF lv_actv_failed EQ abap_true. 

  lo_function->if_fdt_transaction~dequeue( iv_deep = abap_true ). 

  WRITE : / 'Deep activation failed'. 

  LOOP AT lt_message ASSIGNING <ls_message>. 

    MESSAGE ID <ls_message>-msgid TYPE <ls_message>-msgty NUMBER <ls_message>-msgno 
      WITH <ls_message>-msgv1 <ls_message>-msgv2 <ls_message>-msgv3 <ls_message>-msgv4 INTO lv_message.
    WRITE: / 'Reason : -',lv_message. 

  ENDLOOP. 

ELSE. 

  TRY.
   lv_dt_id = lo_decision_table->mv_id. 
   lo_function->if_fdt_transaction~save( iv_deep = abap_true ). 

   WRITE : 'The ID of the decision table created is:' ,lv_dt_id. . 

  CATCH cx_fdt INTO lx_fdt. 

   WRITE : / 'Save failed with exception'.
   LOOP AT lx_fdt->mt_message ASSIGNING <ls_message>.
     WRITE :/ <ls_message>-text. 
    ENDLOOP. 

  ENDTRY. 
  lo_function->if_fdt_transaction~dequeue( iv_deep = abap_true ). 

ENDIF.      

執行後可以看到結果,如下圖,

我的BRF+自學教程(三):動态技術

前往BRF+工作台,按ID搜尋對象

我的BRF+自學教程(三):動态技術

可以查詢到結果,

我的BRF+自學教程(三):動态技術

已經成功建立了決策表,也可以在上文建立的應用下找到這個決策表,

我的BRF+自學教程(三):動态技術

3,動态擷取函數參數

函數的參數是在建立BRF+函數時定義的,在某些場景下,我們需要動态地擷取這些參數,以供程式使用。

比如,使用者建立了若幹公式,公式中存在常量,也存在不同的需要使用者輸入的運算數,這些運算數即函數的參數。程式需要擷取到使用者所選擇的公式中需要輸入的運算數,這樣才能告訴使用者,需要在界面輸入哪些變量的值。

對于這個功能的實作方式,我曾經在SCN上提問,最後自己找到了答案。現把答案轉貼在這裡。

在調用BRG+函數時,需要擷取上下文對象,而函數的參數的名字和ID,就位于上下文對象中的屬性MT_NAME_VALUE中,如下圖

我的BRF+自學教程(三):動态技術

可以為類CL_FDT_CONTEXT建立增強,新增方法GET_MT_NAME_VALUES,以讀取私有屬性MT_NAME_VALUE,

methods GET_MT_NAME_VALUES
    exporting
      value(ET_NAME_VALUE) type HASHED TABLE .      

方法的實作如下,

METHOD get_mt_name_values .

  et_name_value = me->mt_name_value.

ENDMETHOD.      

接下來在自己的程式中調用該方法,

TYPES:
  BEGIN OF s_name_id_value,
    id               TYPE if_fdt_types=>id,
    name             TYPE abap_parmname,
    data_object_type TYPE if_fdt_types=>data_object_type,
    value            TYPE REF TO data,
    value_set        TYPE abap_bool,
  END OF s_name_id_value .
TYPES:
  t_name_id_value TYPE HASHED TABLE OF s_name_id_value
                  WITH UNIQUE KEY name .

DATA: t_name TYPE t_name_id_value.

DATA(lo_fuction) = cl_fdt_factory=>if_fdt_factory~get_instance(
  )->get_function( '005056A4CCA61ED8A8924F0F3F4F1D98' ).
*

DATA(lo_context) = CAST cl_fdt_context( lo_fuction->get_process_context( ) ).

lo_context->get_mt_name_values( IMPORTING et_name_value = t_name ).      

就可以得到相關資訊。

繼續閱讀