MVVM,您要做一项工作!?

想法💡

在过去的几年中, MVVM在iOS社区中赢得了一定的声誉。 几乎所有其他会议都至少有一个发言。 几乎所有其他博客文章都在谈论设计模式,特别是MVVM(就像这样的:p)。 所有这些表明,它必须非常擅长于其工作。 因此,让我们尝试了解它的实际作用。

动机💪

当我们制作一个iOS(或者您可以说一般而言,任何移动/网络)应用程序时,每个屏幕都具有多个UI组件,例如UIViewUITextFieldUILabelUIImageView等等。这些组件需要自己处理很多工作逻辑。 因此,两者之间紧密耦合的机会很大。 现在耦合不好,因为它

  • 增加了进行UI修改的成本
  • 难以对此类代码进行单元测试

因此,为了使它们分离,我们经常尝试使用一些设计模式来添加一些抽象模块化 。 这样的模式之一就是MVVM,它是由Microsoft推出的,它代表的当然是Model-View-ViewModel 。 我不会深入研究MVVM的详细信息,因为您一定不厌倦一遍又一遍地阅读它。 相反,我将谈论它所提供的功能以及我们可以在其他地方应用它。 如果您是第一次收听MVVM,那么Microsoft会提供一些非常好的文档,您可以在此处查阅,或者社区中也有很多不错的帖子。

它能做什么? 🤔

MVVM只有一项工作-它只是将表示逻辑与UI分开。 让我们谈谈这种表示逻辑。 每个UI都需要以一种或另一种形式显示一些数据。 现在,由于数据以原始格式保存,因此无法直接显示。 因此,我们需要对其应用一些装饰性方法,以使其可用于UI。 到目前为止,还不错,但是没有人谈论这些数据从何而来。 它可能来自持久层,或者可能是api调用,或者可能两者兼有。 因此,究竟谁负责获取这些数据。 由于ViewModel是用于装饰数据的“唯一”负责人,因此也许我们可以像一些核心数据助手一样将数据获取逻辑也放入其中,或者可能是一些api逻辑。.aa,我们回到起点,从头开始听起来像控制器。 现在是时候退后一步,只将视图模型保留为表示逻辑了。

关键时刻! 😲

因为,当我们研究MVVM(或此类设计模式)时,这个灰色区域(即谁获得数据)的定义不是很好,因此我们最终将此逻辑放入视图模型或控制器中。 因此,产生了大规模控制器如今的大规模视图模型 。 但是没有人说您不能像提取表示逻辑那样提取数据。 因此,我们还应该从MVVM中学习到的是,我们应该抽象出不同的责任性,并将其置于不同的类别中。 只是MVVM没有谈论数据获取,但是我们可以使用导致MVVM的想法,即每个类都承担单个责任是一件好事。 我们可以通过多种方式实现这一目标,尽管现在我仅使用两种简单的方式进行讨论- concrete classesprotocols

例子🛠️

在第一个示例中,我们将看到如何将Datasource注入到UserListViewModel以便我们可以抽象出将数据提取到单独的类中的逻辑,并使视图模型仅使用它,

 struct User { 

let name: String
}

// to fetch data from persistence/api
struct Datasource {

func items() -> [T] {
return []
}
}

struct UserListViewModel {

// decorated data with business logic
let users : [User]

init(datasource : Datasource) {
// business logic
users = datasource
.items()
.map { "\($0.name.uppercased())" }
.map { "Hey \($0)" }
}
}

我们还可以使用protocol类似的结果,从而使UserListViewModel像数据源一样工作,但其中没有实际的逻辑,

更新:最近我知道以下方法实际上有一个名称– 带有Cake Pattern的依赖注入小世界 😅)

 struct User { 

let name: String
}

// to fetch data from persistence/api
protocol Datasource {

associatedtype Model
}

extension Datasource {

func items() -> [Model] {
return []
}
}


struct UserListViewModel : Datasource {

typealias Model = User

// decorated data with business logic
let users : [User]

init() {
// business logic
users = items()
.map { "\($0.name.uppercased())" }
.map { "Hey \($0)" }
}
}

我们学到了什么!? 🤓

MVVM不仅与分离表示逻辑有关,而且与一般抽象有关(也与其他模式一样)。 让我们不要过分负责任的视图模型或视图控制器。 不要试图使您的逻辑流程适合流行的设计模式。 而是简单地尝试找出仅抽象功能并尝试重用它们的不同方法,这已经足够了。