Skip to content

model view present

model 应该是数据结构 view 负责 UI 展示和与用户的交互 present 作为中间人连接 Model 和 View,处理所有的业务逻辑,将数据请求结果传递给 View,同时响应用户事件并更新 Model

  • 用户输入由 View 接收 View 层(例如 Flutter 中的 Widget)负责展示 UI 和捕获用户交互,比如文本输入、按钮点击等。当用户在界面上输入信息或触发某个动作时,View 会将这些输入事件捕获下来。

  • View 通知 Presenter 处理 在 MVP 模式中,View 本身不处理业务逻辑。捕获到用户输入后,View 会调用 Presenter 的方法,将用户的意图传递过去。例如,在一个 Todo List 应用中,当用户点击“添加任务”按钮时,View 会调用 Presenter 的 addTask(title) 方法。

  • Presenter 处理业务逻辑:修改 Model 数据 Presenter 接收到用户输入后,会根据业务规则对数据进行处理。例如:

    • 验证用户输入是否符合要求;

    • 调用 Model(或者说数据仓库,例如 TaskRepository)对任务数据进行修改(添加、删除、修改状态等);

    • 可能还需要处理数据排序、格式转换等业务逻辑。

  • Presenter 通知 View 更新 UI 操作完 Model 后,Presenter 会调用 View 接口中的方法(比如 showTasks(updatedTasks))传递最新的数据。View 接收到这些数据后,通过调用 setState()(在 Flutter 中)更新自己的本地状态,从而重新构建 UI,展示最新的模型信息。

关于在 view 中重新维护了一份数据:

1. Flutter 的响应式更新机制

在 Flutter 中,UI 的更新依赖于 setState() 的调用。当你调用 setState() 时,Flutter 会重新构建当前 Widget 树中受影响的部分。

  • 单独维护 View 状态:View 中的 tasks 是 Widget 状态的一部分。当 Presenter 调用 showTasks(updatedTasks) 后,View 在 setState() 内更新自己的变量,这样就能通知 Flutter 框架重新构建 UI。

  • 直接引用 Presenter 数据:如果直接在 build 中访问 presenter.tasks(假设 Presenter 维护了一个任务列表),那么当 Presenter 的数据发生变化时,Flutter 的界面没有收到更新的通知,因为它不知道数据刷新了,除非你在外部调用 setState()

  • 明确数据变化的信号:使用回调(showTasks 方法)能明确告诉 View “我有新数据了,请更新 UI”,从而大大增强了界面的可控性和响应性。

2. 职责分离与解耦

在 MVP 架构中:

  • Presenter 负责业务逻辑和数据变更,而

  • View 负责展示数据和响应用户交互。

为了让 View 只专注于 UI 展示,我们希望数据操作与 UI 渲染分离开来。让 Presenter 操作 Model 后,通过回调把最新的变化传递给 View,然后 View 通过自身状态(如 tasks)进行渲染,这样:

  • View 对数据的显示只依赖自己的状态,调用 setState() 能够确保整个 Widget 树按预期更新。

  • Presenter 只需关心数据(例如添加、删除、修改任务)而不涉及界面如何具体更新。

如果直接在 View 中使用 Presenter 的数据,就可能打破这种职责分离,导致耦合度增加,维护和测试都会变得更加困难。

3. 数据一致性与不可变性

通常,我们希望数据流向是单向的,从业务逻辑端(Presenter、Model)传递至 UI 端(View)。这种方式有几个好处:

  • 数据一致性:View 从 Presenter 接收到的更新数据是一份拷贝,这保证了 View 内部的状态与 Presenter 操作后得到的数据是一致的。

  • 不可变性:当你将最新数据传递给 View 时(通过 showTasks(updatedTasks)),你可以确保这份数据是经过计算和验证的,而不是 View 直接操作可能还在变动的数据对象。

4. 数据更新的信号绑定

实现 showTasks 方法的主要目的是作为 Presenter 与 View 之间的通信桥梁:

  • Presenter 调用 view.showTasks(updatedTasks) 时,就意味着 Model 中的数据已经处理完毕,新的数据可用于显示。

  • 此时,在 showTasks 方法中通过 setState() 把这份数据存储到 View 的局部状态中,并触发界面重建。这是一种明确的数据更新信号,确保数据和 UI 始终保持同步。

总结

尽管表面上看起来直接引用 Presenter 的数据(例如直接访问 presenter.tasks)可以节省拷贝步骤,但这种做法不能很好地契合 Flutter 的响应机制,也会增加 View 与业务层之间的耦合度。使用回调的方式保持两者分离:

  • Presenter 完成数据操作后,通过 showTasks(updatedTasks) 通知 View;

  • View 接受到数据后,在 setState() 中更新自己的状态,从而触发 UI 的重新构建。

这种做法提升了应用的可维护性、测试性与扩展性,同时确保 UI 总是正确反映最新的数据状态。

view

通常定义了不同状态的接口

Released under the MIT License.