RxSwift和MVVM最佳实践声明输出变量/函数

RxSwift和MVVM最佳实践声明输出变量/函数,swift,mvvm,rx-swift,Swift,Mvvm,Rx Swift,所以,我将RxSwift与MVVM一起使用,我发现自己有点困惑。原因如下: 我现在的代码 视图模型 这是“好的”。它工作得很好(至少如预期的那样)。 但是,我在读以下书时: 在提示部分,它说: 始终努力将系统或其部件建模为纯功能。这些纯函数可以很容易地进行测试,并可用于修改操作员行为 阅读后,我将ViewModel更改如下: func bindViewModel() { viewModel.outputs.gnomeObject .subscribe { observab

所以,我将RxSwift与MVVM一起使用,我发现自己有点困惑。原因如下:

我现在的代码 视图模型 这是“好的”。它工作得很好(至少如预期的那样)。 但是,我在读以下书时: 在提示部分,它说:

始终努力将系统或其部件建模为纯功能。这些纯函数可以很容易地进行测试,并可用于修改操作员行为

阅读后,我将ViewModel更改如下:

  func bindViewModel() {
    viewModel.outputs.gnomeObject
      .subscribe { observable in self.populate(with: observable.element != nil ? observable.element! : nil) }
      .addDisposableTo(viewModel.disposeBag)
  }
视图模型(已编辑)
内部协议详细信息ViewModelInput{
func viewDidLoad(名称:字符串)
}
内部协议详细信息ViewModelOutput{
func gnomeObject()->可观察
}
协议详细信息ViewModelType{
var disposeBag:disposeBag{get}
变量输入:DetailViewModelInput{get}
变量输出:DetailViewModelOutput{get}
}
结构DetailViewModel:DetailViewModelType,DetailViewModelInput{
设disposeBag=disposeBag()
让viewDidLoadProperty=变量(“”)
func viewDidLoad(名称:字符串){
viewDidLoadProperty.value=名称
}
}
//标记:DetailViewModelOutput
扩展DetailViewModel:DetailViewModelOutput{
func gnomeObject()->可观察{
返回viewDidLoadProperty
.asObservable()
.filter{!$0.isEmpty}
.map{guard let gnome=gnome
.fetch(uniqueValue:$0,forKey:“name”)!as?Gnome else{return nil}
返回侏儒
}
}
}
ViewModels中的区别在于GnomeObject声明,其中一个声明是
var
,“edited”中是
func
。 我担心的是,每次从
ViewController
调用
gnomeObject()
,它都会创建可观察对象的新实例

在这种情况下,最佳做法应该是什么


提前感谢

当他们说应该使用纯函数时,他们的意思是函数(如果可能)对于同一组输入应该有相同的输出,也就是说,如果一个函数用同一组输入调用两次,它应该返回相同的内容两次

这意味着您没有函数调用方不知道的任何隐藏可变状态(例如,拥有该方法的类中的属性)。一切都应该尽可能明确


因此,当涉及到函数时,您应该注意这一点。但是使用属性是完全可以的,就像您在第一个代码中所做的那样,它们不适用于此。

当他们说您应该使用纯函数时,他们的意思是函数(如果可能)对于相同的输入集应该有相同的输出,这意味着,如果一个函数用同一组输入调用了两次,它应该返回相同的内容两次

这意味着您没有函数调用方不知道的任何隐藏可变状态(例如,拥有该方法的类中的属性)。一切都应该尽可能明确


因此,当涉及到函数时,您应该注意这一点。但是使用属性是完全可以的,就像您在第一个代码中所做的那样,它们不适用于此。

嗯,在第一个版本中,
gnomeObject
是一个let,而不是一个var。一旦设置了它,就永远不会更改为其他对象

在第二个版本中,
gnomeObject()
每次调用时都返回不同的对象。因此,这实际上打破了“纯功能”范式。(注意:如果可观察对象是一个结构而不是一个类,那么情况就不是这样了,因为结构没有标识。)


您的第一个示例遵循纯函数概念,而您的第二个版本打破了它。

Hmm,在第一个版本中,
gnomeObject
是一个let,而不是一个var。一旦设置,它就永远不会更改为其他对象

在第二个版本中,
gnomeObject()
每次调用时都返回不同的对象。因此,这实际上打破了“纯功能”范式。(注意:如果可观察对象是一个结构而不是一个类,那么情况就不是这样了,因为结构没有标识。)


第一个示例遵循纯函数概念,而第二个版本打破了它。

如果您希望消除在初始值设定项中实例化gnomeObject的需要,您可以修改第一个示例以使用如下所示的惰性变量:

lazy var gnomeObject: Observable<Gnome?> = self.viewDidLoadProperty
  .asObservable()
  .filter { !$0.isEmpty }
  .map { guard let gnome = Gnome
    .fetch(uniqueValue: $0, forKey: "name")! as? Gnome else { return nil }
    return gnome
}
lazy var gnomeObject:Observable=self.viewDidLoadProperty
.asObservable()
.filter{!$0.isEmpty}
.map{guard let gnome=gnome
.fetch(uniqueValue:$0,forKey:“name”)!as?Gnome else{return nil}
返回侏儒
}

如果希望消除在初始值设定项中实例化gnomeObject的需要,可以修改第一个示例以使用惰性变量,如下所示:

lazy var gnomeObject: Observable<Gnome?> = self.viewDidLoadProperty
  .asObservable()
  .filter { !$0.isEmpty }
  .map { guard let gnome = Gnome
    .fetch(uniqueValue: $0, forKey: "name")! as? Gnome else { return nil }
    return gnome
}
lazy var gnomeObject:Observable=self.viewDidLoadProperty
.asObservable()
.filter{!$0.isEmpty}
.map{guard let gnome=gnome
.fetch(uniqueValue:$0,forKey:“name”)!as?Gnome else{return nil}
返回侏儒
}

这正是我最关心的问题。谢谢这正是我最关心的问题。谢谢
lazy var gnomeObject: Observable<Gnome?> = self.viewDidLoadProperty
  .asObservable()
  .filter { !$0.isEmpty }
  .map { guard let gnome = Gnome
    .fetch(uniqueValue: $0, forKey: "name")! as? Gnome else { return nil }
    return gnome
}