LSP 和 SwiftUI
LSP and SwiftUI
为了使我的代码可测试,我试图通过让我的 SwiftUI 视图依赖于协议而不是具体类型来遵守 Liskov's substitution principle。这使我可以轻松地交换实现,并允许我轻松地构建用于测试的模拟。这是我正在尝试做的一个例子:
protocol DashboardViewModel: ObservableObject {
var orders: [Order] { get }
}
我的 DashboardViewModel
需要将更改传达回其依赖项,因此我还附加了 ObservableObject
作为传递要求。
这似乎是个问题。如果您有关联的类型要求,您不能 实现 LSP。这是我从我的 SwiftUI 视图 class 中得到的错误,该错误取决于我的视图模型:
struct DashboardView: View {
@ObservedObject var viewModel: DashboardViewModel
}
Protocol 'DatastoreProtocol' can only be used as a generic constraint because it has Self or associated type requirements
我最终这样做了:
protocol DashboardViewModel {
var orders: [Order] { get }
var objectWillChange: AnyPublisher<Void, Never> { get }
}
这也需要家属做额外的工作来观察状态变化。这剥夺了使用 属性 包装器的便利——主要是依赖者使用 @ObservedObject
观察状态变化的能力。使用这种替代方法会导致我们编写如下代码:
struct DashboardView: View {
let viewModel: DashboardViewModel
var viewModelSubscriber: AnyCancellable!
// MARK: - Used only to force a re-render of this view
@State private var reload = false
init(viewModel: DashboardViewModel) {
self.viewModel = viewModel
viewModelSubscriber = viewModel.objectWillChange.sink { _ in
self.reload.toggle()
}
}
}
这看起来有点令人反感:
创建了一个仅用于强制更新视图的 @State
变量,因为我们无法利用 SwiftUI 属性 包装器来观察状态变化
创建了一个 AnyCancellable!
变量来保存视图模型对 objectWillChange
的订阅。这是检测来自 DashboardViewModel
的状态变化所必需的
在初始化程序中添加了订阅调用,它只切换 @State
变量以强制从视图模型检索新数据
我觉得应该有更好的方法来处理这个问题。寻求帮助!
解决此问题的一种方法是使您的视图通用:
protocol DashboardViewModel: ObservableObject {
var orders: [Order] { get }
}
struct DashboardView<Model: DashboardViewModel>: View {
@ObservedObject var viewModel: Model
}
为了使我的代码可测试,我试图通过让我的 SwiftUI 视图依赖于协议而不是具体类型来遵守 Liskov's substitution principle。这使我可以轻松地交换实现,并允许我轻松地构建用于测试的模拟。这是我正在尝试做的一个例子:
protocol DashboardViewModel: ObservableObject {
var orders: [Order] { get }
}
我的 DashboardViewModel
需要将更改传达回其依赖项,因此我还附加了 ObservableObject
作为传递要求。
这似乎是个问题。如果您有关联的类型要求,您不能 实现 LSP。这是我从我的 SwiftUI 视图 class 中得到的错误,该错误取决于我的视图模型:
struct DashboardView: View {
@ObservedObject var viewModel: DashboardViewModel
}
Protocol 'DatastoreProtocol' can only be used as a generic constraint because it has Self or associated type requirements
我最终这样做了:
protocol DashboardViewModel {
var orders: [Order] { get }
var objectWillChange: AnyPublisher<Void, Never> { get }
}
这也需要家属做额外的工作来观察状态变化。这剥夺了使用 属性 包装器的便利——主要是依赖者使用 @ObservedObject
观察状态变化的能力。使用这种替代方法会导致我们编写如下代码:
struct DashboardView: View {
let viewModel: DashboardViewModel
var viewModelSubscriber: AnyCancellable!
// MARK: - Used only to force a re-render of this view
@State private var reload = false
init(viewModel: DashboardViewModel) {
self.viewModel = viewModel
viewModelSubscriber = viewModel.objectWillChange.sink { _ in
self.reload.toggle()
}
}
}
这看起来有点令人反感:
创建了一个仅用于强制更新视图的
@State
变量,因为我们无法利用 SwiftUI 属性 包装器来观察状态变化创建了一个
AnyCancellable!
变量来保存视图模型对objectWillChange
的订阅。这是检测来自DashboardViewModel
的状态变化所必需的
在初始化程序中添加了订阅调用,它只切换
@State
变量以强制从视图模型检索新数据
我觉得应该有更好的方法来处理这个问题。寻求帮助!
解决此问题的一种方法是使您的视图通用:
protocol DashboardViewModel: ObservableObject {
var orders: [Order] { get }
}
struct DashboardView<Model: DashboardViewModel>: View {
@ObservedObject var viewModel: Model
}