我的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)前提
要讀懂本節,需要兩方面的知識,
- 基本的ABAP面向對象知識。
- 基本的BRF+知識。
2)需求
通過一個簡單的report程序來創建決策表和它的條目。
3)使用的接口
- IF_FDT_FACTORY
- IF_FDT_DECISION_TABLE
- IF_FDT_FUNCTION
4)創建應用
在工作臺中創建一個應用,接下來將會在這個應用中動態地創建決策表。
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+工作臺,按ID搜索對象
可以查詢到結果,
已經成功創建了決策表,也可以在上文創建的應用下找到這個決策表,
3,動態獲取函數參數
函數的參數是在創建BRF+函數時定義的,在某些場景下,我們需要動態地獲取這些參數,以供程序使用。
比如,用戶創建了若幹公式,公式中存在常量,也存在不同的需要用戶輸入的運算數,這些運算數即函數的參數。程序需要獲取到用戶所選擇的公式中需要輸入的運算數,這樣才能告訴用戶,需要在界面輸入哪些變量的值。
對於這個功能的實現方式,我曾經在SCN上提問,最後自己找到了答案。現把答案轉貼在這裏。
在調用BRG+函數時,需要獲取上下文對象,而函數的參數的名字和ID,就位於上下文對象中的屬性MT_NAME_VALUE中,如下圖
可以為類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 ).
就可以得到相關信息。
我的BRF+自學教程(三):動態技術