使用两种方式进行相同的操作

问题1:用两种方法来构build一个用例(或多个)的正确方法是什么?

例如:

我有一个iOS应用程序中的3个屏幕:
1.地图视图,可以“长按”,并有一个摄像头button。
2.摄像头视图,如果用户在地图视图中点击摄像头button,将显示该视图。
3.地点/别针编辑视图,如果用户“长时间按下”地图视图,或在用户在摄像机视图中select照片之后显示该视图。 这个编辑视图有一个保存button,用于实际创build带有照片和位置的位置(长按坐标或当前位置,以防照相机button被按下)。

标题:创build地点基本stream程:
1.用户在地图上“长按”。
2.应用程序放弃一个临时的引脚,并显示地点编辑视图。
3.用户编辑地点信息并按保存button。
4.应用程序创build的地方,并保存它。

标题:创build地点基本stream程:
1.用户按下加号button。
2.应用程序显示相机视图。
3.用户拍照。
4.应用程序创build与当前位置和图片的地方。

更新根据与bhavik交换的意见。

问题2 🙁根据bhavik的回答)
所以我不需要一个交互者的主持人,我可以有1个交互者和3个主持人/观点。

  1. 就我而言,我应该有一个主持人/地图的视图,这是它开始的地方,
  2. 那么我应该有一个主持人/相机的视图,以防用户点击相机button
  3. 以及一个用于编辑视图的演示者/视图,以在用户“长时间按下”的情况下或者在用户从照相机演示者/视图中select照片并被redirect到相同的编辑视图之后。

那是对的吗?

问题3:我的边界方法是否总是返回void?
在bhavik的例子中,他们正在返回一些东西,但是在VIPER博客和Bob叔叔的video中,他们总是返回无效,结果以交互者调用演示者/控制器的另一种边界方法的forms出现。

问题4:当Bob叔叔的video使用控制器和演示者与交互者进行不同的交互时,VIPER方式不使用控制器,只有演示者与交互者交谈。 我应该采取哪种方法?

问题5:如果我的使用案例是“转到其他屏幕”,它应该甚至有一个交互器? 由于当前视图将告诉其演示者哪个button被按下(视图将到哪个视图),并且当前演示者将会告诉其线框“改变到这个其他线框”。

问题1:用两种方法来构build一个用例(或多个)的正确方法是什么?

在VIPERdevise中,您可以在同一交互器中创build两个适用于每个主用例和替代用例的方法。


问题2 🙁基于bhavik的回答)所以我不需要一个交互者的主持人,我可以有1个交互者和3个主持人/观点。

根据我们的讨论和更新,我认为我更了解它。

  • 演示者/视图不应该与交互者之上的交互。
  • CameraView情况下,演示者/视图可能不与任何交互者交互。
  • 他们是奇才队中的中间人。
  • 多个Presenter / View可以与单个Interactor交互。
  • 交互器不绑定到任何演示者。
  • 单个交互器负责单个用例及其所有备用stream程。 1-1的关系。

因此,您应该为EditPlaceInteractor提供单个EditPlacePresenter/View ,以传递数据放置有或没有照片的数据。


问题3:我的边界方法是否总是返回void?

在bhavik的例子中,他们正在返回一些东西,但是在VIPER博客和Bob叔叔的video中,他们总是返回void,结果以交互者调用演示者/控制器的另一种边界方法的forms出现。

我认为你指的是从Interactor接收结果的下面的Presenter方法。

 - (void)foundUpcomingItems:(NSArray*)upcomingItems 

为了使上面的工作,Interactor将有委托实例,由演示者/控制器通过连线/修补来查找结果或数据。 这意味着演示者/控制器绑定到Interactor,或者它们的引用或返回函数指针在每个Interactor方法调用中传递。 这是由devise?

我认为,Interactor应该根据用例返回数据。 例如Interactor应该返回EditPlaceResult成功或失败。

  • 如果成功,则应包含保存的数据,例如Place ID。
  • 如果失败,应该包括失败原因。

这应该是用例的一部分。 如果不是,那么它不应该返回任何东西。 它将返回void,一个单独的Interactor将由Presenter查询,以检查Map Place是否成功添加。

博客中的参考资料:

  • Presenter包含视图逻辑,用于准备从Interactor 收到的显示内容。
  • 它由演示者负责采集Interactor返回的数据并将其格式化为演示文稿。
  • 演示者从Interactor 接收结果 ,并将结果转换为可在View中高效显示的表单。

问题4:当Bob的叔叔的video使用控制器和演示者与交互者进行不同的交互时,VIPER方式不使用控制器,只有演示者与交互者交谈。 我应该采取哪种方法?

您需要为以下导航定义VIPER路线:

  • 相机button:从MapView导航到CameraView (使用位置)
  • 长按:从MapView导航到EditPlaceView (使用坐标)
  • 拍摄照片:从CameraView导航到EditPlaceView
  • 保存位置:根据具体情况发送交互者请求以保存有/没有照片的地点,如果成功则跳回到MapView
  • 返回button:返回基于导航堆栈的前一个视图

根据VIPER博客,Presenters和Wireframes使用视图控制器和导航控制器。

VIPER线框处理导航,并使视图控制器成为精简,意味着,视图控制机器。

基本上Wireframe抽象了导航控制器,而是提供路由定义。

线框

  • 拥有UINavigationController和UIViewController
  • 负责创buildView / ViewController并将其安装在窗口中
  • 路由在Wireframes中定义,并包含用于描述以哪种顺序显示哪些屏幕的导航逻辑

主持人

  • 使用线框来执行导航
  • 为了保持视图控制器精简,VIPER需要给予View Controller一种当用户采取某些行动时通知相关方的方式 – 演示者来到这里!
  • 视图控制器不应该基于这些动作做出决定,而是应该将这些事件传递给可以 – 演示者应该做出决定的事情!

问题5:如果我的使用案例是“转到其他屏幕”,它应该甚至有一个交互器? 由于当前视图将告诉其演示者哪个button被按下(视图将到哪个视图),并且当前演示者将会告诉其线框“改变到这个其他线框”。

作为用例的一部分,导航可能不需要Interactor。 它只是过渡。 目标演示者可能需要交互者。 例如, CameraView/Presenter不需要Interactor,但EditPlaceView需要保存位置。


总体而言:架构模式背后的思想是将给定的软件应用程序划分为互相连接的部分,以便将信息的内部表示与信息呈现给用户或从用户接受的方式分开。 MVC,MVP,MVVM,VIPER都专注于以某种方式隔离视图,逻辑和导航。

build筑模式在他们打算分解的内容上是有限的。 我们必须明白,架构模式不会分解或隔离一切。 另外,如果一个架构模式将某些责任委托给某个部分,其他人根本就不这样做,或者将多个责任分配给单个部分。

我们被允许扩大或限制隔离和分解的范围,使其certificate原因是合理的,并且不会将超出成本的担忧分开。 您可以select使用导航控制器,并且演示者可以在没有定义线框路线的情况下依赖它们。 这些控制器将负责屏幕之间的导航。

问题: 2个用例与相同的演示者/视图有关时该怎么办? 例如,我有一个主持人/认为

  • 检查用户是否已login
  • 在做任何事之前要求用户位置授权。

回答

用例build模

看来你正在处理非平凡的用例build模场景。

“检查用户login”不需要用例。 同样,如果位置授权不作为configuration设置的一部分进行更新,那么它也不是用例。 它们不是用例build模的好候选,而是其他复杂用例的先决条件或“步骤”。 我认为它们更像是先决条件,然后是“步骤”,而不是个别的“使用案例”本身。

先决条件应该伴随着用例exception和非平凡的“步骤”,通过包含依赖关系提示用例重用。 如果先决条件失败,您可以提供适当的消息和选项来满足这些条件。 对于复杂的步骤,一个用例redirect到其他用例。

这个想法是selectexception,以失败的先决条件中的有效消息来终止用例,或者包括其他用例,这些用例将要求用户先login并恢复当前的用例。

目标是分解复杂的需求并重新使用它们。 引用用例重用 – 包含依赖关系 。 “只要一个用例需要另一个用例的行为,就可以使用包含依赖关系,引入一个新的用例来封装几个用例中出现的类似的逻辑,这是相当普遍的。

在VIPER方式

  • 如果需要,交互者应该知道其他交互者。
  • 规则和先决条件应在交互者或实体中按要求进行validation。
  • 交互者应该为演示者返回适当的数据。
  • 演示者不应该知道其他使用案例的其他演示者。
  • 演示者应该向Wireframe发送请求。
  • 线框应该照顾适当的导航到不同的视图。

从而,

  • CreatePlaceInteractor应依赖于UserLogInInteractorLocationAuthorizationInteractor并在必要时调用它们。
  • 或者CreatePlaceInteractor应该发回Presenter可以解释的数据,并要求Wireframe启动UserLoginPresenter和/或LocationAuthorizationPresenter

这将涉及使用交互者,演示者和线框精心devise的交互和导航控制stream和数据stream。 在这个过程中,你将会挑战很多核心假设和隐藏未处理的系统行为。

由于其他用例也可能包括用户login或授权访问来完成他们的任务,所以这些用例应该包含在内,但不一定是一直执行 – 对UserLogInInteractor和LocationAuthorizationInteractor的条件函数调用。 如果用户已经login并且授权访问已经被允许,他们将被跳过。 当用户selectCameraView时,检查用户login和位置访问。 当用户从MapView直接selectEditView时,只检查用户login。

我认为你应该在你的Interactors中实施先决条件逻辑,Presenter可以使用它来制作演示和导航相关的决定。

考虑以下用例列表:

  • “UC001:用户login”。
  • “UC002:获取位置访问授权”。
  • “UC003:显示地图”。
  • “UC004:在地图上创build地点”。

UC004:在地图上创build地点(带有先决条件)

前提条件:

  1. 用户已login。
  2. 位置访问权限已被授权。

脚步:

  1. 如果不满足前提条件,请运行适当的例外。
  2. “创build地图位置”的其他步骤

UC004-E1:用户没有login或会话过期

脚步:

  1. 向用户显示适当的消息以进行login或会话过期,并提供login选项(button)。
  2. 终止使用案例。 [用户可以select返回地图视图或login屏幕]

UC004-E2:位置访问尚未被授权。

脚步:

  1. 向用户显示适当的信息以设置位置访问权限,并在configuration中提供设置选项(button)。
  2. 终止使用案例。 [用户可以select返回到地图视图或转到设置选项]

UC004:在地图上创build地点(无先决条件)

脚步:

  1. 检查用户已login并且用户会话处于活动状态。 如果没有,运行UC001。
  2. 检查用户是否有授权的位置访问。 如果没有,则显示相应的消息并运行UC002。
  3. “创build地图位置”的其他步骤

UC004> UC001:用户没有login或会话过期

脚步:

  1. 向用户显示适当的消息以login或会话过期并运行UC001
  2. 用户login成功,继续UC004步骤#2
  3. 用户未成功login,终止用例。

UC004> UC002:位置访问权限未被授权

脚步:

  1. 向用户显示适当的消息以进行位置授权并运行UC002
  2. 用户授权的位置成功,继续UC004步骤#3
  3. 用户没有授权访问位置,终止用例。

看起来这两个用例的结果似乎有两个方法 – “使用相机和定位服务”与“手动数据input”具有相同的最终结果,即“使用/不使用图片创build场所”。

“使用相机和定位服务”用例也添加了照片。

但是,我想知道是否有两种方法达到相同的结果或相同的结果被视为单一用例。

如果可以的话,我会把它们devise成单独的用例,否则我会把它们作为主要或默认用例以及其他方法作为实现相同/相同最终结果的替代方法。

用例: 创build地点

基本stream程:使用相机和定位服务

  1. 用户按下加号button。

  2. 应用程序显示相机视图

  3. 用户拍照。

  4. 应用程序创build与当前位置“和图片”的地方。

备用stream程A:使用手动数据input

A.1。 用户在地图上“长按”。

A2。 应用程序放下一个临时针脚并显示位置编辑视图。

A.3。 用户编辑地点信息并按保存button。

A. 4.应用程序创build“无图片”的地方并保存。

这有道理吗?

更新具体细节

负责处理iOS中View的View Controller实际上被视为VIPER中的View。 请参阅“查看”段落。 一个UIViewController或者它的一个子类将实现View协议。 因此,这个控制器是你的观点。

这个想法是你需要将你的Presenter与iOS View或iOS控制器的知识隔离开来。 它应该通过像ViewModels这样简单的数据结构来处理iOS特定的视图和控制器。 如果可以的话,您已经成功地从iOS SDK特定的依赖关系中分离了Presenter,并且您可以直接在您的演示者上编写和运行TDD或unit testing。

然而,更有趣的是,一旦你成功地从View和ViewController中分离出Presenter,你就可以很容易地将Interactor从Presenter中分离出来。 演示者必须以Interactor可接受的forms传递数据。 所以Interactor对Presenter一无所知。 它是独立的,您可以轻松地在命令行,Web或桌面GUI应用程序中使用此交互器(用例)。

我认为每个用例应该有一个Interactor。 如果有用例的替代stream程,交互者将拥有那些替代数据结构的方法。

在你的情况下,CreatePlaceInteractor将有两种方法:

 CreatePlaceWithManualDataEntryResult createPlaceWithManualDataEntry(CreatePlaceWithManualDataEntryRequest) CreatePlaceWithCameraAndLocationServiceResult createPlaceWithCameraAndLocationService(CreatePlaceWithCameraAndLocationServiceRequest) 

将有三个查看/演示者:

  1. CreatePlaceChoicePresenter / View将捕获用户select,并将请求发送到NavigationController或Wireframe,并根据用户的select返回新的Presenter / View。

  2. CreatePlaceWithManualDataEntryPresenter / View将创buildCreatePlaceWithManualDataEntry ViewModel并将其转换为CreatePlaceWithManualDataEntry 请求 ,并将接收CreatePlaceWithManualDataEntry 结果并进行相应处理,以在视图上显示用例结果。

  3. CreatePlaceWithCameraAndLocationServicePresenter / View将创buildCreatePlaceWithCameraAndLocationService ViewModel并将其转换为CreatePlaceWithCameraAndLocationService Request ,并将相应地接收createPlaceWithCameraAndLocationService Result和进程以在视图上显示用例结果。

道歉请求详细,反应,视图模型和方法名称。

用例build模和VIPERdevise注意事项 – 为基于iOS的移动应用程序创build,编辑和查看“放置在地图上”

你的问题

  1. 如果 – 创build,编辑和查看操作在同一个ViewController中结束,该怎么办?

  2. 如果MapViewController使用PlacesInteractor检索位置,并且CurrentLocationInteractor请求用户的位置授权并获取最新的坐标,是不是一个好主意?

将相关逻辑组合成单个Interactor不是问题。 但它不会再是一个“交互作用”。 它将成为MapPlaceManager / MapPlaceService中的一个“服务”或“pipe理者”,它将具有以下方法:

 canCreateMapPlace createMapPlace(Details) getMapPlaceCount getMapPlaceIDs getMapPlaceDetails(ID) canUpdateMapPlace updateMapPlace(ID, NewDetails) 

我认为这个想法是只公开每个用例的预期API,因此Interactor可以清楚地说明Interactor的用户将要做什么。 如果它有多个可以执行不同操作的API,例如创build/编辑/删除地图位置,那么我们必须检查调用者中的方法调用,以了解调用者要做什么。 对我而言,交互器是非常高层次的业务/需求级别接口。 您可以将这些后端服务和pipe理人员隐藏在这些单个交互者中。

你可以把这个概念尽可能地和/或可行的 – 可行的,而不是可能的。 我们画线的地方会有一个极端的地方,而不是太虔诚。 业务系统往往是更正式和有条不紊的给你一个例子。

在你的情况下,当你的主视图上的一个button被改变你的MapPlaceViewMapPlaceEditView ,你正在改变新视图将满足的用例。 这样的就地视图更改是适合移动设备的视图devise考虑因素,而且它也是友好的。 但是,它经常鼓励复杂的GUI和杂乱的演示者逻辑。 如果ViewController / Presenter 在“创build,查看,编辑”之间切换“模式” ,如果它更易于pipe理,更清洁,更简单,那么您就可以继续使用。 这不是完美的,但没有错。 他们是前端参与者,无论如何都有最高的自由度和变化频率。

我发现替代良好的UIdevise,而不是就地字段编辑有用,是“翻转”的意见或任何这样的视图过渡效果。 你可以有一个MainMapPlacePresenter/ViewController ,它有3个子视图 – 创build,编辑和视图。 这个主视图然后负责在这三个视图之间切换。 它可以实现更清洁的导航,清洁的用例实施和整洁的devise。

同样,对于CurrentLocationInteractor ,它做两件事 – 1.请求使用“设备位置服务”的许可和2.使用“设备位置服务”。 现在,它似乎根本不是一个交互器。 这是前端function。 但是可以使用SaveAuthorizationInteractor来保存用户的select。 但那是不同的事情。 我认为越多,交互者负责处理你的系统,而不是你的用户。

演示者完成所有“用户通话”和“决策”工作 – 如果他们需要定位服务,他们可以使用设备API。 您可以创build抽象接口ILocationService和名为LocationService包装实现,它将吸收用户的设备位置服务 – 低层次的实现和平台特定的细节。

在实施方面:你可以有:

 MainPresenter/MainViewController On Load - Show MapView along with Buttons for Edit and Create Map Place MapPresenter/MapViewController On Load - Show Map Navigations - login, authorization, create, edit Interactions - none MapPlaceCreatePresenter/MapPlaceCreateViewController On Load - call MapPlaceCreateInteractor.canCreateMapPlace - Response = {AllGood, UserNotLoggedIn, LocationIsNotAuthorized} Interaction - MapPlaceCreateInteractor.createMapPlace - Responses = {PlaceCreatedSuccessfully} Navigations - Login, Location Authorization, Back to Main View (With Response - UserLoginNeeded, UserAuthorizationForLocationAccessNeeded) MapPlaceUpdatePresenter/MapPlaceUpdateViewController On Load - call MapPlaceUpdateInteractor.canUpdateMapPlace Interaction - MapPlaceUpdateInteractor.updateMapPlace(ExistingMapPlaceID, NewDetails) - Responses = {PlaceUpdatedSuccessfully} Navigations - Login, Location Authorization, Back to Main View (With Response - UserLoginNeeded, UserAuthorizationForLocationAccessNeeded)