决策表模式

2020-09-11 21:37:56

决策表简单、简单、功能强大。你可以在五分钟内教他们,写出一半的时间。您可以查看表格,了解它在说什么,看看它是否与您的问题匹配,并检查是否存在设计缺陷。所以网上基本上没有任何关于他们的信息,这有点奇怪。我前段时间写了一篇介绍信,但我想要更正式一点的。因此,这篇文章将以一种更正式的方式重新介绍核心理念,然后谈论一些你可以用来制作更好表格的技术。

请注意,这是一个理论构思的帖子。我还没有在生产中测试所有的内容,也不能保证它们在实践中工作得很好。买者自负。

决策表将有限枚举的输入映射到输出。输出可以是任何东西:返回值、副作用等等,但是所有可能的输入都必须映射到输出。看一下桌子,你就知道应该发生什么了。这使得它们可以很好地用于案例分析和检查需求。

对于所有这些示例,除非另有说明,否则我们将假设一个输出O。编码输入比编码输出涉及更多的技术。

如果输入的每个可能组合都由一行表示,则决策表是完整的。如果一行输入没有出现两次,并且有两个不同的输出,这是合理的。如果它是完整和健全的,它是有效的。那么,一个有效的表将正好有一行对应于每种可能的输入组合。如果我们有两个布尔输入和一个3值枚举,那么表将有12行。

因为决策表必须满足规则才能被认为是有效的,所以它们被认为是一种形式规范。通过形式化,1)我们可以使用自动化工具分析表的正确性,2)表构造中的错误通常映射到我们设计本身的错误。

决策表也称为Parnas表,特别是当它们用作正式验证的一部分时。

关于决策表的一个常见问题是“为什么不进行模式匹配?”在许多语言中,我们可以这样做:

让patternMatch a b c=Match(a,b,c)与|(true,true,true)->;1|(true,true,false)->;3|(true,false,true)->;7|(true,false,false)->;0|(false,_,_)->;2。

在这种情况下,我们已经有行刑了。那么,如果决策表没有映射到执行,为什么要使用决策表呢?

这是因为决策表是规范,而不是代码。模式匹配适用于在代码中表示单个决策。决策表表示任何抽象级别的决策,无论是代码还是整个系统。它们甚至可以表示高级的非代码决策:

模式匹配是语言实现的一部分。决策表更多的是帮助您进行设计。

也就是说,您还可以将决策表视为控制流构造。决策表在大型企业使用的规则引擎和自动化QA工具中都非常流行。SAP宣传他们的自动决策表。我更感兴趣的是它们作为规范工具的使用,不会进一步讨论它们作为实现构造的使用。

系统可以有无限数量的状态,只要它们映射到有限数量的值。我们可以通过将状态分组为“有效相似的输入”或等价类来做到这一点。然后,我们用类替换原始输入。作为一个有启发性的例子,我们不能做一个“天真的”奇偶表,因为有无限的正数:

由于并非所有可能的输入都可以表示,因此此表不完整,因此无效。我们能做的就是用方程N%2代替数字N,这样就只有有限数量的输入,就可以得到一个有效的表格。

我们可以做的另一件事是将输入划分为区间:给出每个类中数字的下界和上界。另一个陈词滥调的例子是,“这个人在美国能喝酒吗?”

间隔可以是模棱两可的:0-21包括0、21,还是两者都包括?如果两者都有,您如何表示介于0和21之间的实数(独占)?我们可以在这里采用一些数学表示法:(0,21)排除两者,而[0,21]包括两者。

传统上,遗漏的界限隐含着延伸到无穷大,所以我们将[21,∞]写为[21,)。

这些习惯使决策表在视觉上更令人愉悦。我不知道它们是真的让它们变得更好,还是仅仅是装饰。

行应按列值排序。最左边的列是最重要的键,然后是最左边的列,一直向前。换言之:

这使得阅读表格变得更容易。如果您知道您的输入并正在查找输出,则可以对值进行二进制搜索。

所有值都应该以相同的方式排序。如果一列将T放在F之前,其他列不应该将F放在T之前。我强烈喜欢先用T排序。1个。

列的顺序应该与某人通过决策自然推理的顺序相匹配。如果您正在为登录流创建表,那么您可能具有VALID_USER的条件?和正确密码?因为在拥有用户之前谈论密码是没有意义的,所以大多数人都会考虑首先找到用户。该条件的列应该出现在密码列之前。

较少的考虑事项是处理任何字段(见下文)。值为-的列在表中影响较小,应稍后放置。

我认为-规则没有“最重要的列优先”规则那么有美学价值,也没有行规则那么重要。

如果您的某个字段不是布尔值,请显式列举可能性。它可能在表中隐式定义,但这太容易混淆了。

这张桌子齐全吗?我们也不知道。它可能缺少行|F,黄色|!图例不一定是表格本身的一部分,只要读者可以从周围的上下文中推断出来。让读者知道Q可以有值{red,Blue}。

决策表最常见的问题是膨胀:如果您有5个布尔列,那么您的表将有32行。再加上第六个,你就是64岁了。大多数技术都是关于减少膨胀的,例如通过压缩行或分解表。

几乎每个人都会这么做。-意思是“用于任何价值”。这两个表等效:

-非常适合简化表。检查完整性很容易,因为您可以将精简版本映射到完整版本。如果一个表由四个布尔值组成,则|T-|映射到8行。2个。

从美学上看,当价值不遵循时,它看起来更令人愉快-。|T--F|只是看起来很滑稽。有时我在栏目间来回走动,以使虚线排成一条直线。然而,这并不总是可能的,而且它可能会模糊规则的自然流动。

对于-有几个陷阱。首先,您可能不小心声音不佳,因为每行都会扩展到几行。例如:

这是完整的,但并不合理,因为|T T|同时映射到1和2。另一个缺陷是隐式排序。我们可能有。

现在行的顺序很重要了!有人能理解你这里的意思,但现在不再明确了。我更喜欢把东西列在这里。

注释:-通常的意思是“任何”,但这并不总是很清楚。很容易认为这个符号的意思是“未知”或“不知道”。如果你有顾虑,那么最好是直截了当。如果您使用+作为值,则可能应该使用不同的符号。

通过这种方式,决策表可以帮助我们确定逻辑是否不需要某些输入。

如果一个输入对该输出起“一定作用”,那么该输出就依赖于该输入。并不是所有的输出都依赖于所有的输入。

O1只依赖于P,O2只依赖于Q,我们可以将这个表分解成两个更小的表:

分解表使它们变得更简单,但并不总是正确的选择。如果O1和O2在概念上是相关的,这对读者来说可能不太清楚。分解也不一定会减小表的大小。结果可能取决于一个值,而另一个可能同时取决于两个值。

分解该表将得到两个总共有6行的表。另一方面,如果O1和O2在概念上是完全不同的,那么将它们分成单独的表可能会更清楚。一如既往,最好的是高度依赖于上下文。

如果一个人可以限制另一个人的值,则输入是相关的。请遵循以下三条业务规则:

如果表单不存在,请将它们重定向到“开始”页面。

如果表单存在且尚未提交,请重定向到“继续”。

我们可以将其写成两个布尔值:Form_Existes?并提交了吗?如果表单已经存在,则Submit只是一个有意义的布尔值。我们可以将这两个布尔值合并到单个列中,如FORM_STATUS:

对于较大的桌子,这一点可以细分。考虑具有三个值的LOGIN表:PASSWORD_CORRECT?、2AUTH_ENABLED?和2AUTH_CORRECT?如果您不知道密码,那么您是否拥有2auth是无关紧要的,如果您禁用了2auth,那么询问您是否通过了2auth也是毫无意义的。有几种不同的建模方式,都很尴尬。最简单的可能是使用另一个符号,如/,告诉读者这是不可能的:

如果你不想引入一个新的符号,把你的布尔数变成三元组,那么另一个选择就是使用任何一个。这有点欺骗性,因为它暗示该列有一个值,我们只是不关心它是什么。它是有效的,但让逻辑学家非常难过。

在处理依赖输入时,重要的是依赖是事实还是断言。事实是不可能是假的东西。如果我们遇到的情况是错误的,那么软件错误就是我们最不担心的。当条件不兼容并且只有一个条件为真时,最常发生这种情况。在这种情况下,我们可以将列合并到枚举中。

相反,断言是我们构建系统以保证的东西。在现实中,它可能是错误的,但理想情况下,它在我们的系统中永远不会是错误的。

理想情况下是个危险的词。在这种情况下,我想澄清的是,奇怪的组合不是不可能发生的事情,而是不应该发生的事情。这意味着如果它真的发生了,那一定是出了问题,我们应该停下来弄清楚这一点。也许我们编写了自己的锁定机制,但不能完全确定它是否正确,也许我们担心宇宙射线之类的。应明确列举这些可能性,并将输出明确标记为未指定或错误。

这里最简单的答案是重新设计您的系统来处理它,但这可能是不可行的。

如果许多输入或输出依赖于一个输入,则将其分解到单独的表中是有意义的。这两个表应该相互引用。例如,请参见此处。我一直在努力寻找一个好的教学例子,所以我只是为了完整性而提及这一点。

在处理区间时,我们会遇到一个问题,其中不同的条件可能取决于区间到范围的不同划分。

这在处理日期间隔时尤其常见,就像我们在玫瑰形式图中看到的那样。问题是要确保完整性。如果我们有两组不同的间隔覆盖,就会丢失可能值和总行数之间的直接映射。也很容易意外错过一些东西。

一个彻底但臃肿的解决方案是将所有区间组合成一个没有重叠的超集。

这以膨胀和隐藏底层逻辑为代价使表格更加形式化。您还需要注意转换到新分区时的错误,特别是在间隔边界。

实际上,您应该使用转换表或状态机来形式化状态,但是我们可以使用决策表来处理小问题。如果我们希望决策表表示突变,我们需要明确表示表上的新旧值之间的差异。惯例是用素数写变量。如果表格改变了x,我们就把原始值写成x,把新值写成x。

每个表应该只对一个步骤建模。在表格的正文中,x总是指旧值,x';不应该出现。不过,多个输出可以依赖于x,这对于表示同时突变很有用:

如果我们需要表示更复杂的更新,可以将其分解为一个定义x的表和一个定义x';的表:

在决策表中表示状态很快就会变得很笨拙,而且不能很好地扩展到复杂的状态。

决策表具有最少的语法和语义。它们很容易使用。他们也很快达到了极限。

我不喜欢说“当它们不合适的时候不要使用它们”,因为这听起来像是在沾沾自喜地回避。这个答案并没有给你任何洞察力,让你知道他们什么时候是不合适的。我们想具体说明他们什么时候变得不合适。

当整个问题可以建模为离散的、独立的输入时,决策表工作得最好。打破它,一切都会分崩离析。有时,您可以改进问题空间,就像我们在间隔中所做的那样,但这只能让您走到这一步。决策表不合适的其他迹象包括:

当您有复杂的数据结构时。如果其中一个输入是整数列表,那么您的表会很大。

当“随着时间的推移”做出决定时,你必须执行一些效果才能完成决定。DTS对即时决策进行建模。取而代之的是考虑流程图。

当没有简单的方法以表“明显”完整的方式对数据进行编码时。如果您的输入是“具有排名的三个事件”,则完整的表格看起来仍会缺少行。3个。

您需要对决策表的要求有多严格取决于您使用它们的目的。如果您的目标是拥有规范工件,那么您希望尽可能地严格。如果您试图显式地解决设计问题或计划基于该表的高级项目级决策,则需要严格。如果你将决策表作为一种思维增强工具,那么走捷径是可以的。目标是帮助你更好地思考,过度的形式主义并不总是带来洞察力。

决策表简单、简单、功能强大。你可以在五分钟内教他们,写出一半的时间。虽然它们的表现力上限很低,但先试一试没有机会成本。最糟糕的情况是,在意识到它们不适合您的问题之前,您会损失几分钟。

如果您想看到决策表在野外的使用,这里和这里有两个很好的例子。

我希望这能让您更容易访问决策表。如果你对我的其他文章感兴趣,除了这个网站,我还有一份每周两次的软件时事通讯和一个Twitter,它融合了技术、数学、糖果和普通的Twitter内容。

在理想情况下,我们应该有交互的决策表,这样您就可以自由地对行和列进行切片和透视,但是我还没有找到这样做的工具。或者一些Excel插件? [返回]。

每个-可以是两个可能的值之一,因此有2*2*2个值组合。 [返回]。

一共有3个!排序:|1 2 3|、|1 3 2|等。看起来每列可以有3个值中的一个,因此人们可能会感到困惑,认为应该有3^3行。 [返回]