干净的架构控制器可以访问 ViewModel

Clean architecture Controller have access to the ViewModel

我有以下问题,根据这张图

Controller 不应该访问 Presenter 或 ViewModel,但是,我如何通过单击更新视图,即用户单击增加 ViewModel 中的计数器的按钮(因为这是 class 保存视图的状态)然后相应地更新视图。 但是,如果 Controller 可以访问 Presenter,我可以绕过 UseCase 并直接调用 Presenter,然后 class 相应地更新 ViewModel。 我已经阅读了很多文章和方法来做到这一点并且将控制器与 Presenter 混合使用在我看来我会打破单一责任因为控制器将不负责创建 InputData 而是添加逻辑并调用 Presenter . 如何使用图片中的架构来完成此操作_

ViewModel 的目的是以方便 UI 的形式保存数据。

如果点击次数是 UI 所需要的 - 就是这样,但如果我理解正确,你想要的是使这些数据有状态,例如将其保存到数据库并每次获取你需要增加它。

从这个角度来看,您应该为您的数据库(存储库)定义一个接口,当然还有一个适当的实现(可能是一个沉重的数据库或只是一个文本文件)。

最后,您的演示者应该只将最新值(由输出数据调节)投射到 ViewModel 中。

您应该偏离特定 principle/architecture 的时刻是遵循它比不遵循它更不方便的时候。这是因为无论我们谈论的是软件还是实际建筑,项目需求都会塑造架构。一开始尝试针对每种情况进行架构工作是可以的,但这主要是为了让您在不适合给定情况时学习。

不幸的是,我不知道该图像的来源的完整上下文,但从您提到的 SRP 来看,它看起来像是 Bob 叔叔的作品或受到密切启发的东西。我认为您可能犯了与我和其他数千名开发人员在 SRP 上犯的同样的错误。引用 Clean Architecture(Bob 叔叔的书):

Of all of the SOLID principles, the Single Responsibility Principle (SRP) might be the least well understood. That's likely because it has a particularly inappropriate name. It is too easy for programmers to hear the name and then assume it means that a module should do just one thing.

就上下文而言,模块一词并不意味着 Gradle 模块,Bob 叔叔继续将这个词定义为源文件(在 OO 中我们可以将 class 视为控制器) .我将留给您研究 Bob 叔叔对 SRP 的真正含义,因为我不再使用该术语,也不想解释它。 None 是对 Bob 叔叔的不尊重;很喜欢他的作品。

回到问题,如果不了解有关控制器的更多上下文,我很难回答(它只是处理点击并将它们转发给适当的交互器吗?)。我假设这是一个 Android 应用程序,在这种情况下,我会简单地将 Controller 和 Presenter 组合成一个 class。在我看来,这既不违反 Bob 叔叔的 SRP(这是关于将 因不同原因 而改变的事物分开),也不会导致 class 具有低-凝聚力(这关系到 module/class/function/source 的事情)。我意识到这在一定程度上是主观的,但在我看来,处理 UI 交互和为 ViewModel 准备返回的后端数据都符合特定 GUI 功能的表示逻辑的角色。

如果做不到这一点,请忽略我所说的和图表告诉您的内容,只看代码。将它们分开比分开真的能解决更多问题吗?相应地采取行动。

鉴于您提到的要求,我看不出有任何明智的方法可以完全遵循此图,但如果我真的必须将控制器和演示器分开,我会这样做: 在控制器和演示者之间使用观察者(发布者-订阅者)模式(或者可能是演示者和交互者……不是我会做的,但这个图也没有反映我通常如何使用交互者)。您必须保持所有内容松散耦合,并避免在它们之间使用具体引用(即绘制箭头)。

您可以拥有一个 Usecase,它的唯一目的是处理鼠标单击并在单击发生时回调 Presenter。老实说,这似乎是一个荒谬的解决方案。在 UI 中封装递增计数器的业务逻辑的交互器有什么意义?除非你每次都保存那个计数器,否则它甚至看起来都不适合作为术语业务逻辑的开头。

希望对您有所帮助;只是想分享我过去的经验,希望对您有所帮助。

遵循鲍勃叔叔的定义。 单击事件应该是控制器的一部分。 视图不应更新 ViewModel,它只能在演示者更新后读取它。

所有事件都应该转到控制器,然后控制器应该通过将演示对象传递给 use-case 来访问 use-case,并且 use-case 应该调用演示器,更新了 ViewModel。

视图可以访问 Controller 和 Presenter。

或者控制器可以拥有将传递给用例的演示对象