清洁架构 - Robert Martin - 如何连接用例
Posted
技术标签:
【中文标题】清洁架构 - Robert Martin - 如何连接用例【英文标题】:Clean Architecture - Robert Martin - How to connect use cases 【发布时间】:2015-05-07 16:57:36 【问题描述】:我正在尝试实现 Robert Martin 描述的Clean Architecture。 更具体地说,我使用的是 VIPER,它是 ios 版本的 Clean Architecture。
我遇到的问题如下:
用户开始查看带有地点(图钉)的地图。 如果他点击一个按钮,一个图钉就会被放下,然后他会被带到另一个视图以创建(或编辑,如果它是对现有图钉的点击)该位置(或取消)。 在这个其他视图中,用户可以编辑该地点的信息,然后单击“返回”或“完成”(或“编辑”)。 如果他单击“完成”,PlaceDetailsViewController 会向 PlaceDetailsPresenter 发送带有地点信息的消息,并且 PlaceDetailsPresenter 使用 CreatePlaceInteractor 来创建地点。此交互器返回用于识别地点的 GUID。
如果用户在创建地点之前点击返回,他会返回地图,并且放置的图钉会上升并离开(因为它没有 GUID,所以它是一个新地点并且会消失)。 如果他在创建后点击返回,则图钉会停留在那里(因为它应该有一个 GUID)。
我应该如何连接所有这些以及地点信息(包括 GUID)应该存储在哪里? 再澄清一点:
-
谁应该通知 MapPresenter 该图钉是留在那里还是消失?
是 PlaceDetailsPresenter 还是我应该将此信息传递给 PlaceDetailsWireframe -> MapWireframe -> MapPresenter -> MapView ?
在返回之前,此 GUID 应存储在 PlaceDetailsPresenter 中还是 PlaceDetailsViewController 中?
现在这就是我所拥有的:
编辑:
基本上我认为问题在于 VIPER 来自 Robert Martin 的 Clean Architecture,他来自 Web (Rails) 背景,因此他对状态的考虑不多(或者在他的演讲中没有具体说明)。
这主要是我的问题,状态应该存储在哪里,不同的模块应该如何通信,应该通过Wireframe,还是通过数据库,或者通过interactor,或者通过Presenter相互通信,就像这里https://github.com/objcio/issue-13-viper-swift.
【问题讨论】:
我刚开始使用 VIPER,但让演示者相互了解让我感觉不对。我喜欢通过线框/路由器相互通信的模块。很高兴在这里得到纠正,因为我仍在学习这种架构。 我倾向于和你一样思考,虽然我仍然没有找到一个决定性的答案来解决如何在它们之间传递信息,是否通过线框传递数据结构更好(这里有 2 个选项,始终使用相同的大数据结构,或者不断变化以传递最少的必要信息(最终创建更多不可重用的类)),或者将状态保存在交互器中并使用它们来检索信息在另一个演示者中。我发布了另一个 *** 问题,解释了我所说的大数据结构是什么意思,只是还没找到。 ***.com/questions/29054526/… 【参考方案1】:在纯 VIPER 路由器中,应该以协议的形式保存模块输入。并且模块 Presenter 应该符合它。因此,当路由器使用其他模块路由器组装新模块时,它会将其输入传递给它。
然后第二个路由器将输入分配给它的 Presenters 输出。所以基本上第一个模块的 Presenter 成为第二个模块 Presenter 的代表。
因此,在您的情况下,当用户选择一个地点时,MapPresenter 向 MapInteractor 询问 GUID 并告诉 MapRouter 导航到此 GUID 的详细信息。
MapRouter 要求 PlaceDetailsRouter 为这个 GUID 组装 PlaceDetailsModule 并将 MapModuleInput 传递给它。 PlaceDetailsRouter 将 MapModuleInput 分配给 PlaceDetailsPresenter。 PlaceDetailsRouter 将 GUID 放入 PlaceDetailsInteractor
【讨论】:
【参考方案2】:我对 Viper 了解不多,因此无法对此发表评论。但是,系统的总体状态应该保存在实体对象中并由交互器进行操作。 GUI 的详细状态(选择矩形等)应由控制器和演示者之间的特殊连接管理。
在您的情况下,有两个屏幕。地图和地点编辑器。单击地图会导致调用 placePinController。它收集点击的位置和任何其他上下文数据,构造一个 placePinRequest 数据结构并将其传递给 PlacePinInteractor,后者检查 pin 的位置,在必要时对其进行验证,创建一个 Place 实体来记录 pin,构造一个 EditPlaceReponse对象并将其传递给 EditPlacePresenter,它会调出地点编辑器屏幕。
如果在地点编辑器屏幕上单击“完成”按钮,它会调用 EditPlaceController,它将已编辑的数据收集到 EditPlaceRequest 数据结构中,并将其传递给 EditPlaceInteractor。等等。
您特别询问了图钉的 GUID。这将由 Place 实体创建并传递回 editPlacePresenter PlacePinInteractor。
【讨论】:
也许我不是很清楚,但我想说的是:点击发生在 MapPresenter 中,但是如果用户在 PlaceDetailsViewController 中按下“完成”(即喜欢风景)。当用户单击“完成”时,该位置被创建。如果用户点击“返回”,那么它应该返回到 MapPresenter,然后不会关闭图钉(或者如果它没有创建,则关闭它)。 我所说的状态是指在地图中临时创建的图钉之类的东西。这样当我们从 PlaceDetailsViewController/Presenter 回来时,我们就知道要关闭或维护哪个引脚(并提供要关联的 GUID)。 将“完成”和“返回”视为不同的用例(不同的交互者)。 “后退”交互者会做什么? @RobertMartin 假设点击地图会创建一个实体。您假设仅在创建过程完成后才应创建实体。两种解决方案都有效,但需要不同的沟通渠道。 Bob 的方法更简单如果您可以创建具有缺失值且不违反业务规则的实体:如果单击后退按钮取消创建,则删除该实体。如果这不起作用,请使用临时 DTO 传递。 (NSDictionary 可能会这样做,但我建议使用自定义值对象进行数据清理。)以上是关于清洁架构 - Robert Martin - 如何连接用例的主要内容,如果未能解决你的问题,请参考以下文章
markdown Robert C. Martin的“清洁代码”摘要