我的BRF+自学教程(三):动态技术

时间:2023-01-05 11:07:56

开发者们可以在编程中使用各种动态技术,比如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 = . 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 = . 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 = . 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 = . 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 = . 
ls_column-object_id = lv_element2_id.
ls_column-is_result = abap_false. INSERT ls_column INTO TABLE lts_column. * 填充结果列元素
ls_column-col_no = .
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 = .
ls_table_data-col_no = .
ls_range-position = .
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 = .
ls_table_data-col_no = .
ls_range-position = .
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 = . ls_table_data-col_no = . 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 ).

就可以得到相关信息。