如何解决如何正确组织这个基于 Rx 的反应式状态机?
请注意:尽管我在示例中使用了 Swift-lang,但我鼓励您尝试帮助我,即使您了解 RxJava 和 rxjs 或任何其他 Rx 实现。
问题:我有一个以命令方式编写的状态机。我想使用 Rx 被动地重写,但我正在努力想出正确的方法。
当前的命令式系统如下所示:
var state: State
var MetaState: MetaState
func updateMetaStateIfNeeded() {
if Bool.random() {
MetaState = MetaState()
}
}
func tick() -> State {
updateMetaStateIfNeeded()
let newState = State(currentState: state,MetaState: MetaState)
self.state = newState
return newState
}
到目前为止我想出了什么:
let tick = PublishSubject<Void>()
let MetaState = BehaviorSubject<MetaState>()
let state = BehaviorSubject<State>()
let tickAfterMetaStateUpdate = PublishSubject<Void>()
tick -> withLatestFrom MetaState -> map to new MetaState if update needed or the old one -> put result into MetaState AND tickWithUpdatedMetaState
tickAfterMetaStateUpdate -> withLatestFrom state AND MetaState -> map State(currentState: state,MetaState: MetaState) -> put result into state
(The users of this API subscribe to state subject,which will be updated every tick)
我对我想出的这个结果不满意。我想知道是否可以使用 scan
或 reduce
重写它,以便不使用 Subjects 并且它是一个普通的 Observable。
@Daniel T. 回答后的更新:
哇,这正是正确的解决方案!我已准备好分析:
- 有 2 个状态机,一个用于
MetaState
更新,一个用于state
更新 - 他们每个人都有自己的开始状态,我们不需要指定
-
MetaState
的输入字母是Void
,仅存在一个勾号。对于state
,它是第一个状态机的MetaState
输出
因此,根据这种理解,我使用 2 次扫描重写了系统:
let state = tick
.scan(MetaState()) { currentMetaState,void in
if Bool.random() {
return MetaState()
}
return currentMetaState
}
.scan(State()) { currentState,currentMetaState in
State(currentState: state,MetaState: MetaState)
}
WINRAR!头脑风暴.jpg!实际上,现在事后看来似乎很明显... :) 谢谢,谢谢,谢谢!
解决方法
嗯...是的,scan
是实现 state machine 的 go to 运算符。为此,您需要一组有限的状态(通常作为结构体实现,但也可以是枚举)、起始状态(通常使用 State 结构体上的默认构造函数实现)、输入字母表(通常作为结构体实现)枚举,通常称为事件、命令或操作的某种变体)和转换函数(传递给 scan
的闭包。)
所以这里真正的问题是改变状态的命令/动作是什么?大多数情况下,操作是由用户操作产生的,但您的问题并没有真正说明操作是什么,输入字母表是什么......
遗憾的是,问题中没有足够的信息来提供更多细节。
-- 更新 --
抱歉,您的更新不正确。传递给 scan
的闭包应该是纯的,第一个肯定不是。如果 MetaState.init()
是纯的,则 Bool.random()
不是。
您应该将不纯闭包传递给 Observable 的唯一时间是当您创建新的 Observable、使用最终结果或在 do
内时。
对您拥有的内容的简单修复如下所示:
let state = tick
.flatMap { // this closure is an Observable factory. You can do impure things here.
Observable.just(Bool.random() ? MetaState() : nil)
}
.scan(State()) { currentState,metaState in // this closure should be pure.
if let metaState = metaState {
return State(currentState: currentState,metaState: metaState)
}
else {
return currentState
}
}
这确实是一个小改动,但它更清楚地显示了代码的意图,并明确了哪些可以进行单元测试,哪些不能进行单元测试。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。