我正在学习Rx(特别是RxSwift),我有一个关于架构、层和边界的问题。
我习惯于分层体系结构(数据、域、表示),通常在MVP或VIPER上。对于这个项目,我使用的是MVVM,这是Reactive应用程序的推荐架构。这些是我目前的合作者:
**** Presentation ******************************************
________________________
| |
| GameViewController |
| |
| ____________ |
| | | |
| | BoardView | |
| |____________| |
| |
|________________________|
|
|
|/
________________________
| |
| GameViewModel |
|________________________|
|
**** Domain **************** | *****************************
|/
________________________
| |
| GameController |
|________________________|
当用户点击(移动)时,BoardView
会发出一个事件,GameViewController
正在观察该事件,它会调用GameViewModel
中的一个方法,该方法与GameController
通信以检查移动是否正确,然后发出链中每个人正在观察的另一个事件。最后,BoardView
会根据移动的正确性绘制其内容。
我的问题是,这个流程正确吗?我必须坚持这种做事方式吗?还是有一种更适合我的被动方式?例如,也许BoardView
可以在不涉及视图控制器的情况下直接与视图模型对话,并且不存在边界突破或"违反规则"的情况。
我有点迷失在Rx更好的架构方面,MVVM很简单,但要使其稳固,你必须创建更多的合作者,然后可观察性链可能会有点过度设计。
任何帮助都将不胜感激!谢谢:)
在一个编写良好的反应式应用程序中,您的逻辑往往被封装在许多无状态(静态)函数中,而不是对象中。从而使代码更具声明性。
例如,您的GameViewModel应该接受它将观察到的许多输入可观测值,并为视图订阅生成许多输出可观测值。
struct MyViewModel {
let output: Observable<OutState>
init(input: Observable<InState>) {
output = input.map {
// transform input state into output state
}
}
}
请注意,在上文中,变换是一个纯函数。还要注意,对象本身是多余的。它可以很容易地成为一个函数:
func myOutput(input: Observable<InState>) -> Observable<OutState> {
return input.map {
// transform input state into output state
}
}
当然,这个转换块本身可以是一个函数。
func transform(in: InState) -> OutState {
}
它非常容易测试,并且自然地封装了应用程序的特定用例。
通常,当您使用数据驱动的体系结构(如MVVM
)时,会有多个Service
对象来实现您的业务逻辑。在Rx世界中,您订阅了您的服务,服务将DTOs
推送到您的控制器中,并将其映射到ViewModel
中。如果您要使用MVVM
,我真的建议您研究SOA
,因为ViewModel
只是DTO
的装饰器,可以被不同的视图接受。您的业务逻辑应该包含在服务中。
Rx只是推送接口及其随时间的转换的集合,使您的对象能够更加封装和自给自足。Rx方法允许您的对象在业务逻辑需要时提供数据,而不是在控制器决定时提取数据。我认为在使用Rx时,不可能在MVVM
或VIPER
中为每个业务逻辑场景定义100%拟合规则。只要运用常识,看看你的对象什么时候向接收者推送信息是有意义的,而不是在特定的时间推送信息。
附带说明:我真的建议您远离任何数据驱动的对象定义方法(无论是MVVM
还是VIPER
)。原因如下。