我知道这是一个非常古老的问题,但我确实发现自己处于类似情况,并且我认为在某些情况下,FSM 可能会在复杂的布尔逻辑中提供一些帮助。
我在blog post of mine 中概述了一个示例,其中我描述了在遍历(循环)简单字符串的标记时使用状态机。
在这种情况下的优点是,解析过程中的每个标记都没有一个专用的布尔标志,语法树中的其他路径需要防范,我可以将每个标记作为事件提供给 FSM 并让机器陷入不同的状态。我在处理过程中使用 Statechart 操作来构建小操作码,然后最终根据最终状态中止或继续编译生成的操作码。
您必须阅读这篇文章才能了解我上面所说的内容。但它的要点是,在某些情况下,一系列布尔值可以转换为事件名称并传递给 FSM 进行处理。在另一个示例中,我必须根据一组压倒性的布尔逻辑来选择需要呈现的 UI 状态。
由于某些标志优先于其他标志,因此生成的逻辑树如下所示:
图 1 — UML 活动图示例
这导致代码看起来有点像这样:
图 2 — 复杂的布尔逻辑代码
let presenterFlags = {
showSpecialTypeTab: model.type === 'special',
showDefaultWarning: (model.type === 'special' && model.isDefault && !settings.enforced),
showDefaultInBrokenStateWarning: (model.type === 'special' && model.isDefault && settings.enforce),
disableSaveButton: (model.type === 'special' && model.isDefault && !settings.enforce) || !model.canWrite,
disableCancelButton: (model.type === 'special' && model.isDefault && !settings.enforce) || !model.canWrite,
disableDeleteButton: (model.type === 'special' && model.isDefault) || !model.canWrite,
disableEnforcementToggle: (model.type === 'special' && model.isDefault && !settings.enforced) || !model.canWrite,
disableAnotherToggle: (model.type === 'special' && model.isDefault) || !model.canWrite,
};
这对我来说太过分了,我的大脑无法承受。所以我倾向于使用 FSM,它产生了如下状态图:
图 3 — UML 状态图示例
使用XState,代码可能看起来像这样:
图 4 — XState 机器示例
let booleanMachine = Machine({
id: 'ExamplePresentationFlags',
strict: true,
initial: 'conditional',
context: {},
states: {
conditional: {
on: {
'EVALUATE': [
{ target: 'SpecialExample', cond: 'isSpecialExample' },
{ target: 'GenaricExample' },
],
},
},
GenaricExample: {
initial: 'conditional',
states: {
conditional: {
on: {
'': [
{ target: 'ReadOnly', cond: 'canNotWrite' },
{ target: 'Default', cond: 'isDefault' },
{ target: 'Writable' },
],
},
},
Writable: {},
Default: {},
ReadOnly: {
meta: {
disableSaveButton: true,
disableCancelButton: true,
disableDeleteButton: true,
},
},
},
},
SpecialExample: {
initial: 'conditional',
meta: { showSpecialTypeTab: true },
states: {
conditional: {
on: {
'': [
{ target: 'ReadOnly', cond: 'canNotWrite' },
{ target: 'Default', cond: 'isDefault' },
{ target: 'Writable' },
],
},
},
Writable: {},
ReadOnly: {
meta: {
disableSaveButton: true,
disableCancelButton: true,
disableDeleteButton: true,
disableAnotherToggle: true,
},
},
Default: {
initial: 'conditional',
states: {
conditional: {
on: {
'': [
{ target: 'Enforced', cond: 'isEnforced' },
{ target: 'Unenforced' },
],
},
},
Unenforced: {
meta: {
exampleWarning: 'default-read-only',
disableSaveButton: true,
disableCancelButton: true,
disableDeleteButton: true,
disableAnotherToggle: true,
},
},
Enforced: {
meta: {
exampleWarning: 'special-default-broken-enforce-state',
disableSaveButton: false,
disableCancelButton: false,
disableDeleteButton: true,
disableAnotherToggle: true,
},
},
},
},
},
},
},
}, {
guards: {
isSpecialExample: (ctx) => ctx.exampleType === 'special',
canNotWrite: (ctx) => !ctx.canWrite,
isEnforced: (ctx) => ctx.isEnforced,
isDefault: (ctx) => ctx.isDefault,
isNotDefault: (ctx) => !ctx.isDefault,
},
});
使用类似于 reducer 的功能:
图 5 — XState reducer 函数示例
function presentorFlags({ canWrite, model, settings }) {
let machine = booleanMachine.withContext({
canWrite,
exampleType: model.type,
isEnforced: settings.enforced,
isDefault: model.isDefault,
});
let { meta } = machine.transition(machine.initialState, 'EVALUATE');
return Object.keys(meta)
.reduce((acc, key) => ({ ...acc, ...meta[key] }), {});
}
理解我同意这个例子是非常规的,而且更大。它确实使我能够理解逻辑,尤其是使用手头的可视化工具(即图 3)。当时,它让我能够概念化所有边缘案例状态,而不必担心 UI 的每个状态就视图代码而言意味着什么。相反,我可以专注于状态本身以及使机器进入该状态的逻辑。然后我给减速器实际的布尔值,让机器完成工作。我得到的只是一组易于放置在我的模板中的 UI 标志。
再一次,也许这不是更好,也可能是这样。关键是可以使用状态机来表达布尔逻辑。