如何正确组织这个基于 Rx 的反应式状态机?

如何解决如何正确组织这个基于 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)

我对我想出的这个结果不满意。我想知道是否可以使用 scanreduce 重写它,以便不使用 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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?