具有2种方式的Use Case用于相同的操作

时间:2014-09-24 07:30:22

标签: ios design-patterns architecture use-case

问题1:使用2种方法执行相同操作构建用例(或多个用例)的正确方法是什么?

例如:

我在iOS应用中有3个屏幕:
1.地图视图,可以“长按”并具有相机按钮 2.摄像机视图,如果用户在地图视图中点击摄像机按钮,则会显示该视图 3.位置/引脚编辑视图,如果用户“长按”地图视图,或在用户在摄像机视图中选择照片后显示。此编辑视图有一个保存按钮,用于实际创建带有照片和位置的位置(长按坐标或当前位置,以防按下相机按钮)。

标题:创建地点 基本流程:
 1.用户在地图上“长按”  2.应用程序删除临时图钉并显示场所编辑视图  3.用户编辑地点信息并按下保存按钮  4.应用创建场所并保存。

标题:创建地点 基本流程:
 1.用户按下加号按钮  2.应用程序显示摄像机视图  3.用户拍照  4.应用程序创建具有当前位置和图片的地方。

根据与bhavik交换的评论进行更新。

问题2:(根据bhavik的回答)
因此,我不需要为一个交互者准确地提供一个演示者,我可以有1个交互者和3个演示者/视图。

  1. 在我的情况下,我应该有一个地图的演示者/视图,这是它开始的地方,
  2. 然后我应该有一个相机的演示者/视图,以防用户点击相机按钮
  3. 以及编辑视图的一个演示者/视图,以防用户“长按”或用户从摄像头演示者/视图中选择照片并重定向到同一编辑视图。
  4. 这是对的吗?

    问题3:我的交互操作员的边界方法是否总是返回无效? 在bhavik的例子中,他们正在返回一些东西,但是在VIPER blogthe uncle Bob's video中,它们总是返回void,结果以另一个边界方法的形式出现,交互器调用presenter / controller。

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

    问题5:如果我的用例类似于“转到其他屏幕”,它是否应该有一个交互器?由于当前视图将告诉其演示者按下了什么按钮(要查看的视图),并且此当前演示者将告诉其线框“更改为此另一个线框”。

4 个答案:

答案 0 :(得分:12)

问题1:使用2种方法执行相同操作构建用例(或多个用例)的正确方法是什么?

在VIPER设计中,您可以在同一个Interactor中创建两个适用于用例的每个主要和替代的方法。


问题2:(基于bhavik的回答) 因此,我不需要为一个交互者准确地提供一个演示者,我可以有1个交互者和3个演示者/视图。

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

  • Presenter / View不应与Interactor上的内容进行交互。
  • Presenter / View可能根本不与任何Interactor交互,例如CameraView
  • 他们是奇才队的中间观点。
  • 多个Presenter / View可以与单个Interactor进行交互。
  • Interactor不会绑定到任何Presenter。
  • Single Interactor负责单个用例及其所有备用流程。 1-1关系。

因此,您应该为EditPlacePresenter/View提供单EditPlaceInteractor个传递数据的地方数据,无论是否有照片。


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

在bhavik的例子中,他们正在返回一些东西,但在VIPER博客和叔叔Bob的视频中,他们总是返回无效,结果以另一个边界方法的形式出现,交互者调用了演示者/控制器。

我认为您指的是接收Interactor结果的以下Presenter方法。

- (void)foundUpcomingItems:(NSArray*)upcomingItems

为了使上述工作正常,交互者将拥有委托实例,由Presenter / Controller查找结果或数据进行连线/修补。这意味着Presenter / Controller与Interactor绑定,或者在每个Interactor方法调用中传递它们的引用或返回函数指针。这是设计的吗?

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

  • 如果成功,则应包括已保存的数据,例如地点ID。
  • 如果失败,应包括失败原因。

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

博客中的参考文献:

  • Presenter包含用于准备内容以便从交互者中显示收到的视图逻辑。
  • 由演示者决定是否使用Interactor返回的数据 并对其进行格式化以进行演示。
  • Presenter 从Interactor接收结果,并将结果转换为有效显示在视图中的表单。

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

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

  • 相机按钮:从MapView导航至CameraView(使用位置)
  • 长按:从MapView导航至EditPlaceView(使用坐标)
  • 拍摄的照片:从CameraView导航至EditPlaceView
  • 保存位置:根据具体情况向Interactor发送保存带/不带照片的地方的请求,如果成功则跳回MapView
  • 后退按钮:返回基于导航堆栈的上一个视图

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

VIPER线框处理导航并使视图控制器变得精简,平均,视图控制机器。

基本上,线框抽象出导航控制器,而是提供路线定义。

<强>线框

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

<强>演示

  • 使用线框执行导航
  • 为了让视图控制器保持精简状态,VIPER需要为视图控制器提供一种方式,以便在用户采取某些操作时通知相关方 - 演示者来到这里!
  • 视图控制器不应该根据这些操作做出决定,但它应该将这些事件传递给可以 - 演示者应该做出决定的事情!

问题5:如果我的用例类似于&#34;转到其他屏幕&#34;,它是否应该有一个交互器?由于当前视图将告诉其演示者按下了什么按钮(要查看的视图),此当前演示者将告诉其线框&#34;更改为其他线框&#34;。

没有。作为用例的一部分导航可能不需要Interactor。它只是过渡。目标Presenter可能需要一个Interactor。例如,CameraView/Presenter不需要Interactor,但EditPlaceView需要保存该位置。


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

建筑模式受限于他们打算分解的内容。我们必须明白,架构模式不会分解或隔离所有内容。此外,如果一个架构模式将某些职责委托给某个部分,那么其他人根本不会这样做或者将多个职责分配给单个部分。

我们被允许将隔离和分解扩展或限制到它证明原因合理的程度,并且不会对超出成本的关注点进行不必要的分离。您可以选择使用导航控制器,您的演示者可以在不定义线框路径的情况下依赖它们。然后这些控制器将负责屏幕之间的导航。

答案 1 :(得分:3)

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

  • 检查用户是否已登录以及
  • 在执行任何其他操作之前请求用户位置授权。

<强>答案

用例建模

您似乎正在处理非平凡的用例建模方案。

&#34;检查用户登录&#34;不需要用例。同样,如果位置授权没有作为配置设置的一部分进行更新,那么它也不是用例。它们不是用例建模的良好候选者,而是前提条件或步骤&#34;在其他复杂的用例中。我认为他们更像是先决条件然后&#34;步骤&#34;当然不是个人&#34;用例&#34;自己。

前提条件应该伴随着用例异常和非平凡的步骤&#34;通过include-dependencies提示用例重用。如果先决条件失败,您可以提供适当的消息和选项以满足这些条件。对于复杂的步骤,一个用例重定向到其他用例。

我们的想法是选择在失败的前置条件下使用有效消息终止用例的异常,或者包括要求用户先登录并恢复当前用例的其他用例。

目标是打破复杂的需求并重新使用它们。引自Use Case Reuse - Include Dependency。 &#34;只要一个用例需要另一个用例的行为,就可以使用include依赖项。引入一个新的用例,它封装了在几个用例中出现的类似逻辑,这很常见。&#34;

以VIPER方式

  • 如果需要,交互者应该了解其他交互者。
  • 规则和先决条件应根据需要在Interactors或实体中进行验证。
  • 交互者应该为演示者返回适当的数据。
  • 演示者不应该了解其他用例中的其他演示者。
  • 演示者应向Wireframe发送请求。
  • 线框应该对不同的视图进行适当的导航。

因此,

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

这将涉及使用Interactors,Presenters和Wireframes精心设计的控制流和数据流的交互和导航。在此过程中,您将挑战许多核心假设并隐藏未处理的系统行为。

由于其他用例也可能包括用户登录或授权访问以完成其任务,因此应包括这些用例,但不一定要一直执行 - 条件函数调用UserLogInInteractor和LocationAuthorizationInteractor。如果用户已登录并且已允许授权访问,则将跳过它们。当用户选择CameraView时,请检查用户登录和位置访问。当用户直接从MapView选择EditView时,请检查是否仅用户登录。

我认为你应该在Interactors中实现前置条件逻辑,而Presenter可以使用它来制作与演示和导航相关的决策。

考虑以下用例列表:

  • &#34; UC001:用户登录&#34;。
  • &#34; UC002:获取位置访问授权&#34;。
  • &#34; UC003:显示地图&#34;。
  • &#34; UC004:在地图上创建地点&#34;。

UC004:在地图上创建位置(带前置条件)

前提条件:

  1. 用户已登录。
  2. 位置访问已获得授权。
  3. 步骤:

    1. 如果不满足前提条件,请运行相应的例外。
    2. &#34;创建地图位置&#34;
    3. 的其他步骤

      UC004-E1:用户未登录或会话已过期

      步骤:

      1. 向用户显示适当的消息,以便登录或会话过期,并提供登录选项(按钮)。
      2. 终止用例。 [用户可以选择返回地图视图或进入登录屏幕]
      3. UC004-E2:位置访问尚未获得授权。

        步骤:

        1. 向用户显示相应的消息,以设置位置访问权限并在配置中提供设置选项(按钮)。
        2. 终止用例。 [用户可以选择返回地图视图或转到设置选项]
        3. UC004:在地图上创建地点(没有先决条件)

          步骤:

          1. 检查用户是否已登录且用户会话是否处于活动状态。如果没有,请运行UC001。
          2. 检查用户是否先前已授权位置访问权限。如果没有,请显示相应的消息并运行UC002。
          3. &#34;创建地图位置&#34;
          4. 的其他步骤

            UC004&gt; UC001:用户未登录或会话已过期

            步骤:

            1. 向用户显示适当的消息以登录或会话已过期并运行UC001
            2. 用户登录成功,继续UC004步骤#2
            3. 用户未成功登录,终止用例。
            4. UC004&gt; UC002:未授权位置访问

              步骤:

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

答案 2 :(得分:2)

用例建模和VIPER设计注意事项 - 为基于iOS的移动应用程序创建,编辑和查看“地图上的位置”

您的问题

  1. 如果 - 在同一个ViewController中最终创建,编辑和查看操作,该怎么办?

  2. MapViewController是否使用PlacesInteractor来检索场所和CurrentLocationInteractor以请求用户的位置授权并获取最新的坐标,这是一个好主意吗?

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

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

    我认为这个想法是只针对每个用例展示预期的API,因此也就是Interactor - 它可以清楚地说明Interactor的用户将使用它做什么。如果它有多个API可以执行不同的操作,例如创建/编辑/删除地图位置,那么我们必须检查调用者中的方法调用以了解调用者将要执行的操作。从这个意义上来说,交互者对我来说是非常高级别的 - 业务/需求级别接口。您可以在这些单独的Interactors中隐藏后端服务和管理器。

    你可以尽可能地和/或可行地采用这个概念 - 可行而且可行。在我们画线的时候会有一个极端,而不是过于虔诚地追随它。商业系统往往更正式,更有条理地为您举例。

    在您的情况下,当您在主视图上按下一个按钮,将MapPlaceView更改为MapPlaceEditView时,您将更改新视图将要满足的用例。这种就地视图更改是适用于移动设备的视图设计注意事项,并且使用方便。但是,它经常鼓励复杂的GUI和凌乱的演示者逻辑。如果ViewController / Presenter在“创建,查看,编辑”之间切换“模式”是可管理的,更清晰,更容易的 - 那么你很高兴。它并不完美,但没有错。他们是前端参与者,无论如何都拥有最高的自由度和变化频率。

    我发现一个替代好的UI设计而不是就地字段编辑,是“翻转”视图或任何此类视图过渡效果。您可以拥有MainMapPlacePresenter/ViewController,它有3个子视图 - 用于创建,编辑和查看。然后,该主视图负责在这三个视图之间切换。它可以实现更清晰的导航,更清洁的用例实现和整洁的设计。

    类似于CurrentLocationInteractor,它做了两件事 - 1.请求使用“设备位置服务”的权限和2.使用“设备位置服务”。现在,它似乎根本不是一个Interactor。它是前端功能。但您可以使用SaveAuthorizationInteractor来保存用户的选择。但那是不同的事情。我认为,Interactors负责处理您的系统而不是您的用户。

    Presenter完成所有“用户交谈”和“决策”工作 - 如果需要,可以使用设备API。定位服务。您可以创建抽象接口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)
    

答案 3 :(得分:1)

这两个用例似乎具有相同的最终结果,即“使用/不使用图片创建一个地方”,有两种方式 - “使用相机和位置服务”与“手动数据输入”。

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

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

如果可以的话,我会将两者设计为单独的用例,否则我会将其作为主要或默认用例和其他方法作为替代方法来实现相同/相同的最终结果。

使用案例:创建地点

基本流程:使用相机和位置服务

  1. 用户按下加号按钮。

  2. 应用程序显示摄像机视图。

  3. 用户拍照。

  4. App创建当前位置“和图片”的地方。

  5. 备用流程A:使用手动数据输入

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

    A.2。应用程序删除临时图钉并显示场所编辑视图。

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

    一个。 4.应用程序创建“没有图片”的地方并保存。

    这有意义吗?

    具体细节的更新

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

    您的想法是,您需要将Presenter与iOS View或iOS控制器的知识隔离开来。它应该通过像ViewModel这样的普通数据结构来处理iOS特定的视图和控制器。如果您可以这样做,则已成功将Presenter与iOS SDK特定的依赖关系隔离,如果需要,您可以直接在Presenters上编写和运行TDD或单元测试。

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

    我认为每个用例应该有一个Interactor。如果有用例的替代流程,则交互器将具有使用这些替代数据结构的方法。

    在您的情况下,CreatePlaceInteractor将有两个方法:

    CreatePlaceWithManualDataEntryResult createPlaceWithManualDataEntry(CreatePlaceWithManualDataEntryRequest)
    
    CreatePlaceWithCameraAndLocationServiceResult createPlaceWithCameraAndLocationService(CreatePlaceWithCameraAndLocationServiceRequest)
    

    将有三个观看/演示者:

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

    2. CreatePlaceWithManualDataEntryPresenter / View将创建并转换 CreatePlaceWithManualDataEntry ViewModel into CreatePlaceWithManualDataEntry 请求并将收到 CreatePlaceWithManualDataEntry 结果并相应处理以在视图上显示用例结果。

    3. CreatePlaceWithCameraAndLocationServicePresenter / View将创建CreatePlaceWithCameraAndLocationService ViewModel 并将其转换为 CreatePlaceWithCameraAndLocationService 请求并将收到 createPlaceWithCameraAndLocationService 结果并相应处理以在视图上显示用例结果。

    4. 对请求,共鸣,视图模型和方法名称的详细信息表示道歉。