谷歌iosched

谷歌 iosched MVP_FRAMEWORK翻译

文末是全部原文,链接:https://github.com/google/iosched/blob/master/doc/MVP_FRAMEWORK.md
谷歌前几天更新了iosched的源码,是不错的学习素材。
水平有限,借助工具翻译,调了个别句子跟排版,凑合着看,有精力看源码有更好的理解再调整。

#什么是MVP ?
MVP(ModelViewPresenter)是一种关注分离解耦的软件架构。presenter充当模型和视图之间的桥梁,负责处理表示逻辑。

#为什么使用MVP
MVP架构提高了代码的可测试性、可读性和可伸缩性。这些改进是通过分离关注点来实现的:它支持更容易的测试(包括单元和UI),更小的类大小,这样可以更容易地改变获取数据的方式或UI。

mvp-diagra

#我们如何实现MVP

  • DataQueryUserAction 概念的抽象:
    DataQuery对应于上面的MVP图中的请求模型更新,UserAction对应于用户操作事件。抽象已经使用了接口QueryEnumUserActionEnum来完成。

    • AUserAction发生在视图中,由Presenter监听,并被发送到Model;Model处理与操作相关的任何数据更改,然后通过回调通知Presenter,它自己通知视图(View)
  • A DataQuery发生在数据查询。presenter创建了一个初始数据查询列表,该列表是在模型(Model)上请求的;模型(Model)运行查询,然后通过回调通知presenter,它本身通知视图(View)。

  • ModelViewpresenter:这些都是用泛型进行的,所以从代码中可以清楚地看到,所有的类集成在一起构成一个特性。注意:由于view是Android中的一个类,因此mvp框架使用名称UpdatableView

  • 对于presenter的单一实现类:通过使用上面的 QueryUserAction,所有特性都可以为presenter使用相同的实现类(PresenterImpl),从而减少新特性所需的新类的数量。PresenterImpl可能有一个或多个视图

  • 使用加载器管理器回调Model的抽象实现类:我们的大部分功能都使用一个内容提供程序和加载器管理器来获取数据,因此我们创建了一个抽象实现类,名为ModelWithLoaderManager。需要关注加载器管理器实现细节。通过隐藏这个内部的模型(model),而不是直接实现Fragment或Activity,这意味着我们若是改变这一切,我们只需要改变的模型(Model)类功能,不需要改变Fragment(UpdatableView)或Presenter

  • 框架实现类是单元测试:PresenterImpl和加载器管理器模型(ModelWithLoaderManager)的具体方法是[单元]测试,所以不需要特殊的特性

  • 使用注解来加载模型特性:ModelProvider允许注入存根模型,从而使我们能够编写确定性的UI测试。

  • iosched-mvp-framework

为新特性编写代码

  • 创建一个FeatureActivity类,以及至少一个 FeatureFragment 类。所有FeatureFragment类都应该实现Updatable View
  • 创建一个FeatureModel 类。是否使用加载器管理器(LoaderManager)获取数据?如果是,扩展模型与加载器管理器(ModelWithLoaderManager)。如果不是,就直接实现模型(Model)接口(参考MyScheduleModel)。
  • 定义数据查询(data queries) (在FeatureModel 类中)实现QueryEnum的publicenum (枚举)
  • user actions (用户操作)定义为在Feature Model类中实现UserActionEnum的公开枚举

  • 创建PresenterImpl的实例。您的活动中有多个片段实例吗?如果是,请使用片段管理器从您的特性活动中使用片段管理器实例化演示器Impl,以获得对所有特性片段实例的引用;如果不是,则直接从特征片段的单一视图构造函数实例化它。

  • 为您的模型应用TDD: :创建一个单元测试类,为每个查询和用户操作编写测试。PresenterImpl确保模型不能用无效的查询或用户操作来调用,因此不需要对它们进行测试。如果需要帮助,请参阅SessionDetailModelTest

  • 对您的视图应用TDD:单元测试对于复杂的类和方法是有用的,确保应用程序的唯一方法是,从用户的视图(UI测试)中对功能进行测试。
  • 创建一个Android测试类。如果需要帮助,请参考ExploreIOActivityTest作为示例。
  • 创建一个存根模型,它扩展了您的特性模型,并覆盖了查询内容提供程序的任何机制。引用StubExploreIOModel为例。
  • 创建虚假数据或使用已经创建的假数据(参见mockdata package中的类)。要创建新的虚假数据,您可以在[OutputMockData]中找到有用的方法。此步骤是最耗时的步骤,但它确保您具有确定性的UI测试(即不依赖于服务器数据的测试)。
  • 创建虚假数据或使用已经创建的假数据(参见mockdata package中的类)。要创建新的虚假数据,您可以在[输出模拟数据]中找到有用的方法。此步骤是最耗时的步骤,但它确保您具有确定性的UI测试(即不依赖于服务器数据的测试)。
  • 使用Android Test Support Library编写UI测试。参考SessionFeedbackActivityTestSessionDetailActivity_InScheduleSessionTest作为一个例子。
  • 注意,每个测试类使用相同的存根模型和相同的模拟数据,因此如果需要对不同的数据进行测试,那么创建几个测试类。

What is MVP?

ModelViewPresenter is a software architecture that separates concerns. The presenter acts as the
bridge between the model and view, taking care of the presentation logic.

mvp-diagra

Why MVP?

The MVP architecture improves testability, readability and scalability of the code. Those
improvements are achieved by separating concerns: it enables easier testing (both unit and UI),
smaller class sizes, and it makes it easier to change the way the data is obtained or the UI.

How we have implemented MVP

The main features of our mvp framework are:

  • Abstraction of DataQuery and UserAction concepts: DataQuery corresponds to Requests
    model update
    in the MVP diagram above, and UserAction corresponds to User action events. The
    abstraction has been done using an interface for each, QueryEnum and UserActionEnum.

    • A UserAction happens in a view, is listened to by the Presenter, and is dispatched to the
      Model; the Model processes any data change related to the action, then, via a callback, notifies
      the Presenter which itself notifies the view.
    • A DataQuery happens when the view is loaded. The presenter is created with a list of initial
      DataQuerys, which are requested on the Model; the Model runs the queries, then, via a
      callback, notifies the Presenter which itself notifies the view.
  • Interfaces for Model, View and Presenter: those are typed using generics, so it’s clear from
    the code what are all the classes integrating together to form a feature. Note: as View is a class
    in Android, the name UpdatableView is used for our mvp framework.

  • Single implementation class for Presenter: by using the abstraction above for Query and
    UserAction, all features can use the same implementation class for the Presenter
    (PresenterImpl), thus reducing the number of new classes required for new features.
    A PresenterImpl may have one or more Views.

  • Abstract implementation class for Model using LoaderManager callbacks: most of our features
    use a Content Provider and LoaderManager with a CursorLoader to get the data, so we have created
    an abstract implementation class called ModelWithLoaderManager. This takes care of the LoaderManager
    implementation detail. By hiding this inside the model, rather than implementing the LoaderManager
    directly on the Fragment or Activity, it means that should we decide to change this, we would need
    only change the Model class for our features, with no change required in the Fragment
    (UpdatableView) or Presenter.

  • Framework implementation classes are unit tested: PresenterImpl and concrete methods in
    ModelWithLoaderManager are unit tested, so individual features do not need to.

  • Features use injection to load their model: The ModelProvider allows for stub models to be
    injected, thus enabling us to write deterministic UI tests.

iosched-mvp-framework

Required coding for a new feature

  • Create a FeatureActivity class, and at least one FeatureFragment class. All
    FeatureFragment classes should implement UpdatableView.

  • Create a FeatureModel class. Do you use LoaderManager to get the data? If yes, extend
    ModelWithLoaderManager. If not, implement the Model interface directly (refer to
    MyScheduleModel for an example).

  • Define the data queries as a public enum that implements QueryEnum, inside the
    FeatureModel class

  • Define the user actions as a public enum that implements UserActionEnum, inside the
    FeatureModel class

  • Create an instance of the PresenterImpl. Do you have multiple fragment instances in your
    activity? If yes, instantiate the PresenterImpl with the multiple views constructor from your
    FeatureActivity, using the FragmentManager to get references to all your FeatureFragment
    instances; if not, instantiate it with the single view constructor from the FeatureFragment
    directly.

  • Apply TDD for your model: create a unit test class, write tests for each query and user
    action. The PresenterImpl ensures that the Model cannot be called with invalid query or user
    action so no need to test for those. Refer to SessionDetailModelTest as an example if you need
    help to get started.

  • Apply TDD for your view: While unit tests are useful for complex classes and methods, the only
    way to ensure an app is maintainable is to have tests for the functionality from a user’s point of view,
    ie UI tests.

    • Create an Android test class. Refer to ExploreIOActivityTest as an example if you need
      help to get started.
    • Create a stub model that extends your FeatureModel and override any mechanism that queries
      the content provider for data. Refer to StubExploreIOModel as an example.
    • Create fake data or use already created fake data (refer to classes in mockdata package).
      To create new fake data, you may find methods in OutputMockData useful. This step is the
      most time consuming but it ensures you have deterministic UI tests (ie tests that don’t rely on
      the server data).
    • Write UI tests using the Android Test Support Library. Refer to SessionFeedbackActivityTest
      and SessionDetailActivity_InScheduleSessionTest as an example.
    • Note that each test class will use the same stub model with the same mock data, so create
      several test classes if you need tests for different data.