X+Logic
应用程序逻辑,特别是业务规则,通过代码来维护会很麻烦和耗时。如果所有的应用程序逻辑都是硬编码的,它将可能导致大量的if-then-else或select-case代码段,亦可能会变成巨大的噩梦。
而相较去维护大量的字符串比较、布尔测试或存储过程,开发人员有更多重要问题、事务需要去处理。已经有许多尝试规则引擎(也称为“推理引擎”),但是大多需要编更加怪异的代码,而这让非开发人员来维护也是很困难的。
从应用程序中解耦业务逻辑肯定会产生更强大和可维护的代码,方便非开发人员(如相关领域专家)来维护数据和规则模型,只需它合乎逻辑并且易于理解。当然,也可以将应用程序部分数据通过数据库SQL来实现,但这仍然需要大量的开发工作来定义应用程序中每个独特的“规则驱动”事件所需的数据模型和查询,更不用提网络或有限资源环境中的DB性能瓶颈。
x+Logic,是一个非常强大和灵活的解决方案,满足应用程序逻辑和自动化需求;它是基于表格的规则引擎,采用决策表,即数学领域或电气/计算机工程师常说的“真值表”,类似电子表格,定义了问题给定的输入条件的可能解决方案。
决策表评估器(x+Logic)顺序地读取表规则,比对给定的输入系列参数,确定输出。在下面的例子中,从上到下,从左到右,根据规则数据所提供的信息创建一个真值表。
规则表本身是无状态的,但很容易确定和提供必要的变量。发生的基本步骤是:
1. 用户通过代码确定它需要决策表(可以使用xLogicDesigner可视化完成)。
2. 用户通过BizXML为这些输入系列指定相应的输入/输出参数。
3. x+Logic加载表并获取表中的输入系列。
4. x+Logic评估决策表并返回结果并按BizXML中要求输出。
例如,假设我们需要计算会员的级别,
|
会员 类别 |
界定标准 |
|
A类 |
最近一年内内消费金额在2000元以上∕或年度消费频次在4次及以上用户 |
|
B类 |
最近一年消费在1000-1999元以内∕或年度消费频次在2次及以上用户 |
|
C类 |
最近一年消费在1-999金额∕或年度消费频次在1次及以上用户 |
|
D1类 |
最近1-2年未消费 |
|
D2类 |
最近2-3年未消费 |
|
D3类 |
最近3年以上为消费 |
我们向您展示如下电子表格:

不用写一行代码,也无需提供问题更多背景信息,数据关系与逻辑是清楚的。 从程序上来说,我们可以把它看成一系列的if-then-else语句或者SQL语句实现。
无论是if-then-else语句或者SQL语句都不可能像表格更直观,让非开发人员更容易理解,并且现实中用表格逻辑来维护大量实用工程和/或业务数据会更方便。使用DB来驱动规则在某种程度上确实将逻辑从源代码解耦,但必须使用服务器回调来检索数据,从而降低应用程序速度。如果逻辑需要改变,例如我们增加新的界定标准比如有无退货、差评率等怎么办?对于DB,您可能需要调整数据库表结构、SQL语句、存储过程。或者你有一些界定组合,然后添加这些输入参数到硬编码方法中,这将拥有一大堆单调且易错的编码。但如果你已经使用了x+Logic的决策表,那么除了复制新的规则XML文件(非开发人员/某些领域专家可自行编辑)之外,你可能没有什么工作要做,只需要添加新的输入参数到BizXML代码。
逻辑代码(memberlevel.xml)
<?xml version="1.0" encoding="UTF-8"?>
<Tables>
<Table name="memberlevel">
<Inputs>
<Attr>annualamount</Attr>
<Value op="32" id="3">2000</Value>
<Value/>
<Value op="32" id="4">1000</Value>
<Value/>
<Value op="32" id="5">1</Value>
<Value/>
<Value/>
<Value/>
<Value/>
<Value/>
<Value/>
<Value/>
</Inputs>
<Inputs>
<Attr>annualtimes</Attr>
<Value/>
<Value op="32" id="6">4</Value>
<Value/>
<Value op="32" id="7">2</Value>
<Value/>
<Value op="32" id="5">1</Value>
<Value/>
<Value/>
<Value/>
<Value/>
<Value/>
<Value/>
</Inputs>
<Inputs>
<Attr>noneyears</Attr>
<Value/>
<Value/>
<Value/>
<Value/>
<Value/>
<Value/>
<Value op="1" id="5">1</Value>
<Value op="1" id="7">2</Value>
<Value op="1" id="8">3</Value>
<Value/>
<Value/>
<Value/>
</Inputs>
<Outputs>
<Attr>level</Attr>
<Value op="1" id="9">A</Value>
<Value op="1" id="9">A</Value>
<Value op="1" id="10">B</Value>
<Value op="1" id="10">B</Value>
<Value op="1" id="11">C</Value>
<Value op="1" id="11">C</Value>
<Value op="1" id="12">D1</Value>
<Value op="1" id="13">D2</Value>
<Value op="1" id="14">D3</Value>
<Value op="1" id="14">D3</Value>
<Value/>
<Value/>
</Outputs>
</Table>
</Tables>
BizXML代码(logicdemo.xml)<相关语法参见“BizXML”规范之“EvalTbl”章节>
<?xml version="1.0" ?>
<WndTap Name="logicdemo" Text="表格逻辑">
<var name="MemberLevelTbl"><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="memberlevel.xml"/></var>
<Ctrls>
<GroupBox Name="GroupBoxLogicM" Text="会员逻辑演示"><Ctrls>
<Label Name="lb_input1" Text="输入1:" Size="50,20"/><ComboBox Name="annualamount" Size="100,100" Items="100|1000|2000" SelectedIndex="0"/>
<BR/><Label Name="lb_input2" Text="输入2:" Size="50,20"/><ComboBox Name="annualtimes" Size="100,100" Items="1|2|5" SelectedIndex="1"/>
<BR/><Label Name="lb_input3" Text="输入3:" Size="50,20"/><ComboBox Name="noneyears" Size="100,100" Items="1|2|5" SelectedIndex="1"/>
<BR/><Label Name="lb_output" Text="输出:" Size="50,20"/><TextBox Name="EvalTblTest1" Size="50,20" ToolTip="EvalTableTest"></TextBox>
<Button Name="cal" Size="50,20" Text="计算">
<OnClick>
<EvalTbl rules="%{MemberLevelTbl}" table="memberlevel">
<inputs annualamount="%{annualamount}" annualtimes="%{annualtimes}" noneyears="%{noneyears}"/><outputs level="EvalTblTest1"/></EvalTbl>
</OnClick>
</Button>
</Ctrls></GroupBox>
</Ctrls>
</WndTap>

上面示例的源代码,规则表存储为XML,并且由xLogicDesigner程序“编译规则”保存在一个XML文件中(例如)。存储在规则引擎内的所有值都是本地字符串,因为它们被序列化为XML。
为了优化性能,可以通过对规则表中的所有存储值和传入的任何输入值进行数字标记来避免字符串比较。这样,它只是大多数时间比较数字。
在运行时,一旦传递了表的输入值,它将依次分解为一系列布尔单元格,其中每个单元格的值为true或false。任何留空的输入单元格总是被视为真。因此,如果我们传递值annualamount =“1000”和annualtimes =“1”,noneyears=“”我们以前的决策表看起来很像一个逻辑AND门:
|
annualamount |
annualtimes |
noneyears |
|
F |
T |
T |
|
T |
F |
T |
|
T |
T |
T |
|
T |
F |
T |
|
T |
T |
T |
|
T |
T |
T |
|
T |
T |
F |
|
T |
T |
F |
|
T |
T |
F |
<==这是我们的解,对应于令牌化的存储器值10,其字符串值是“B”;
当输入值为(2000,2,1)时,对应于令牌化的存储器值9,
|
annualamount |
annualtimes |
noneyears |
|
T |
T |
T |
输出值为“A”,系统会自动将逻辑引擎的输出值“A”赋值个“EvalTblTest1”(第四个控件TextBox),接收输出值的也可以是变量Var,用于后续操作。

“测试规则”——设计完规则可测试规则的合理性,按顺序输入相应参数值,点击“确定”继续,最后显示系列参数值及相应结果;“取消”中止测试。
“编译规则”——设计完规则将规则输出保存至xml文件以提供给x+应用使用。
“JavaScript”——编辑/修改该表逻辑的JavaScript代码。
“打开文件”——打开现有的逻辑表规则文件编辑或测试。


您还可以对输入值执行以下布尔运算:
|
EQUALS( = ) |
1 |
(0x1) |
|
NOT_EQUAL( !=,<> ) |
2 |
(0x2) |
|
LESS_THAN( < ) |
4 |
(0x4) |
|
LESS_THAN_EQUAL( <= ) |
8 |
(0x8) |
|
GREATER_THAN( > ) |
16 |
(0x10) |
|
GREATER_THAN_EQUAL( >= ) |
32 |
(0x20) |
|
RANGE_INCLUSIVE( [] ) |
64 |
(0x40) |
|
RANGE_END_INCLUSIVE( (] ) |
128 |
(0x80) |
|
RANGE_START_INCLUSIVE( [) ) |
256 |
(0x100) |
|
RANGE_NOT_INCLUSIVE( () ) |
512 |
(0x200) |
|
JAVASCRIPT |
1024 |
(0x400) |
|
GETS |
2048 |
(0x800) |
>:大于,alpha或数字
<:less-than,alpha或numeric
!=或<>:不等于
[x,y]:值的范围,包括端点
(x,y):值的范围,独占结束。您可以混合使用[]和()
=:不明确地使用,这是规则单元格的默认行为,不要求字符串去标记化
您可以在输入单元格中指定多个值,这称为“OR”,测试将根据输入值检查它们,就像代码中的“或”一样:if(value1=test||value2=test||value3=test)then do something ...在单元格中缩写为value1|value2|value3。
在输出单元中,“|”分隔符的行为就像一个“和”(&&)。这样,您的解决方案可以返回多个值。表评估的结果总是作为数组返回(C++中的向量)。
您可以使用get()关键字在运行时将值动态连接到单元格中。例如,假设我们需要一个用于价格表显示的文本输出,并且希望按规则驱动文本。我们可能想读它:“你已经购买X项价格P”。在表中,我们可以创建一个输出:“你已经购买了get(QtyOfItems)of price get(ItemPrice)”,其中QtyOfItems和ItemPrice是我的应用程序状态中将提供的值。您还可以在输入中使用get()创建更动态的测试。代替“> 55”的输入单元,它可以是“> get(SomeValue)”。
在输出单元格中支持使用JavaScript(所有端口)的运行时脚本,因此您可以执行数学计算并实现更高级的规则。您的输出单元格将只包含正确的关键字js()或JavaScript代码段。对于单行代码,它可能看起来像:
Js(return(56 * 3).toString())//注意:对于单行代码,你可以省略“return”和“.toString()”
如果你的代码有多行/函数,请确保它显式地返回一个字符串在结尾或你会得到一个类型转换错误。当与get()关键字如:js(get(value1)*get(value2))结合使用时更有用。