gpt4 book ai didi

ios - 构建 iOS 网络应用程序(REST 客户端)的最佳架构方法

转载 作者:行者123 更新时间:2023-12-01 16:13:24 25 4
gpt4 key购买 nike

关闭。这个问题是 opinion-based 。它目前不接受答案。












想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文来回答。

3个月前关闭。



Improve this question




我是一名有一定经验的 iOS 开发人员,这个问题对我来说真的很有趣。我在这个主题上看到了很多不同的资源和 Material ,但我仍然感到困惑。 iOS 联网应用程序的最佳架构是什么?我的意思是基本的抽象框架、模式,它适用于每个网络应用程序,无论是只有几个服务器请求的小型应用程序还是复杂的 REST 客户端。 Apple 建议使用 MVC 作为所有 iOS 应用程序的基本架构方法,但无论是 MVC 还是更现代的 MVVM 模式都没有解释网络逻辑代码的放置位置以及如何组织它。
我是否需要开发类似 MVCS( SService )之类的东西,并且在这个 Service 层中将所有 0x2341341 和其他复杂的 0x2341 请求放在其他复杂的角度?在做了一些研究之后,我找到了两种基本的方法。 Here有人建议创建为每个网络请求的Web服务API(如API类或LoginRequest类等)的基本要求抽象类PostCommentRequest并且除了所有继承来创建一些全局性的网络管理器,它封装了一个单独的类常见的网络代码和其他偏好(可能是 AbstractBaseRequest 定制或 AFNetworking 调优,如果我们有复杂的对象映射和持久性,甚至是自己的具有标准 API 的网络通信实现)。但这种方法对我来说似乎是一种开销。另一种方法是像第一种方法一样使用一些单例 RestKit 调度程序或管理器类,但不是为每个请求创建类,而是将每个请求封装为该管理器类的实例公共(public)方法,例如: API 、 0x2518122311343 等。那么,最好和正确的方法是什么?还有其他我不知道的有趣方法吗?
我是否应该为所有这些网络内容创建另一个层,例如 fetchContactsloginUser 层或我的 Service 架构之上的任何层,或者该层应该集成(注入(inject))到现有的 0x23138 层中NetworkProvider ?
我知道存在漂亮的方法,或者像 Facebook 客户端或 LinkedIn 客户端这样的移动怪物如何处理成倍增长的网络逻辑复杂性?
我知道这个问题没有准确和正式的答案。这个问题的目标是从有经验的 iOS 开发者那里收集最有趣的方法。最佳建议方法将被标记为已接受并获得声誉奖励,其他方法将被投票。这主要是一个理论和研究问题。我想了解 iOS 中网络应用程序的基本、抽象和正确的架构方法。我希望有经验的开发人员详细解释。

最佳答案

I want to understand basic, abstract and correct architectural approach for networking applications in iOS


构建应用程序架构没有“最好的”或“最正确的”方法。这是一项非常有创意的工作。您应该始终选择最直接和可扩展的架构,这对于任何开始为您的项目或团队中的其他开发人员工作的开发人员来说都是清楚的,但我同意,可以有“好”和“坏”之分“建筑学。
你说:

collect the most interesting approaches from experienced iOS developers


我不认为我的方法是最有趣或最正确的,但我已经在几个项目中使用了它并且对它感到满意。这是你上面提到的方法的混合方法,也是我自己研究工作的改进。我对构建方法的问题很感兴趣,这些方法结合了几种众所周知的模式和习语。我认为很多 Fowler's enterprise patterns 可以成功应用于移动应用程序。这里是最有趣的,我们可以申请(在我看来)创建iOS应用程序体系结构的列表: Service LayerUnit Of WorkRemote FacadeData Transfer ObjectGatewayLayer SupertypeSpecial CaseDomain Model。您应该始终正确设计模型层,并且始终不要忘记持久性(它可以显着提高您的应用程序的性能)。您可以为此使用 Core Data。但是您不要忘记, Core Data 不是 ORM 或数据库,而是具有持久性的对象图管理器作为它的一个不错选择。因此,通常 Core Data 对您的需求来说太重了,您可以查看新的解决方案,例如 RealmCouchbase Lite ,或者基于原始 SQLite 或 .1224138 构建您自己的轻量级对象映射/持久层。此外,我建议您熟悉 LevelDBDomain Driven Design
起初,我认为,我们应该为网络创建另一个层,因为我们不想要胖 Controller 或沉重、不堪重负的模型。我不相信那些 fat model, skinny controller 的东西。但我 确实相信 中的 skinny everything 方法,因为任何类(class)都不应该胖。所有的网络一般都可以抽象为业务逻辑,因此我们应该有另一层,我们可以把它放在这里。 CQRS 是我们需要的:

It encapsulates the application's business logic, controlling transactions and coordinating responses in the implementation of its operations.


在我们的 MVC 领域中, Service Layer 就像是域模型和 Controller 之间的中介。这种方法有一个相当相似的变体,称为 Service Layer,其中 Store 实际上是我们的 Service 层。 Store 出售模型实例并处理网络、缓存等。我想提一下,您不应该在服务层中编写所有网络和业务逻辑。这也可以被认为是一个糟糕的设计。有关更多信息,请查看 MVCSAnemic 域模型。模型中可以处理一些服务方法和业务逻辑,因此它将是一个“丰富”(具有行为)的模型。
我总是广泛使用两个库: RichAFNetworking 2.0 。我认为对于与网络和 Web 服务交互或包含复杂 UI 逻辑的任何现代应用程序来说,它都是必备的。
架构
首先我创建了一个通用的 APIClient 类,它是 ReactiveCocoa 的子类。这是应用程序中所有网络的主力:所有服务类都将实际的 REST 请求委托(delegate)给它。它包含我在特定应用程序中需要的 HTTP 客户端的所有自定义:SSL 固定、错误处理和创建简单的 NSError 对象,其中包含详细的失败原因和所有 API 和连接错误的描述(在这种情况下, Controller 将能够显示正确的用户的消息)、设置请求和响应序列化程序、http header 和其他与网络相关的内容。然后我在逻辑上将所有 API 请求划分为子服务,或者更准确地说, AFHTTPSessionManager : UserSerivcesCommonServices 、 0x251812231343123 和 1231343123 相应地实现了业务逻辑 1343141 和 1231 到 131341 的业务。这些微服务中的每一个都是一个单独的类。它们一起形成一个 SecurityServices 。这些类包含每个 API 请求的方法,处理域模型,并始终向调用者返回 FriendsServices 和解析的响应模型或 Service Layer
我想提一下,如果你有复杂的模型序列化逻辑——然后为它创建另一个层:类似于 microservices 但更一般的,例如JSON/XML -> 模型映射器。如果您有缓存:那么也将其创建为单独的层/服务(您不应将业务逻辑与缓存混合使用)。为什么?因为正确的缓存层可能非常复杂,有自己的陷阱。人们实现复杂的逻辑来获得有效的、可预测的缓存,例如具有基于 profunctor 的投影的幺半群缓存。您可以阅读这个名为 Data Mapper 的美丽库以了解更多信息。并且不要忘记 Core Data 可以真正帮助您解决所有缓存问题,并且可以让您编写更少的逻辑。此外,如果您在 RACSignal 和服务器请求模型之间有一些逻辑,您可以使用 Carlos 模式,它将检索数据并将其映射到实体模型的逻辑与作用于模型的业务逻辑分开。因此,即使您拥有基于 Core Data 的架构,我也建议使用 Repository 模式。存储库可以抽象事物,如 NSErrorNSManagedObjectContextNSFetchRequest 等简单的方法,如 NSEntityDescription 或 0x234131
在服务层完成所有这些操作之后,调用者( View Controller )可以对响应执行一些复杂的异步操作:信号操作、链接、映射等,借助 NSPredicate 原语,或者只是订阅它并在响应中显示结果看法。我注入(inject)在所有这些服务类我 getRepository,将特定的服务调用转化为相应的 putReactiveCocoaAPIClientGET等请求REST端点。在这种情况下, POST 被隐式传递给所有 Controller ,您可以通过参数化 PUT 服务类来明确地传递。如果您想对特定服务类使用 DELETE 的不同自定义,这可能是有意义的,但是如果您出于某些原因不想要额外的副本,或者您确定您总是会使用一个特定的实例(没有自定义) APIClient - 使其成为单例,但不要,请不要将服务类设为单例。
然后每个带有 DI 的 View Controller 再次注入(inject)它需要的服务类,调用适当的服务方法并将它们的结果与 UI 逻辑组合起来。对于依赖注入(inject),我喜欢使用 Dependency Injection 或更强大的框架 BloodMagic 。我从不使用单例,上帝 APIClient 类或其他错误的东西。因为如果你给你的类(class)打电话 APIClient ,这表明你不知道它的用途,它是一个 Typhoon 。单例也是一种反模式,在 中,大多数 情况(罕见的除外)是错误的解决方案。仅当满足以下所有三个标准时才应考虑单例:
  • 单实例所有权无法合理分配;
  • 需要延迟初始化;
  • 没有另外提供全局访问。

  • 在我们的例子中,单个实例的所有权不是问题,而且我们在将我们的神管理器划分为服务后也不需要全局访问,因为现在只有一个或几个专用 Controller 需要特定的服务(例如 APIClient Controller 需要 APIManagerWhatever 等等在)。
    我们应该始终尊重 bad design choice 中的 WhateverManager 原则并使用 SOLID ,所以不要将所有服务方法和网络调用都放在一个类中,因为这很疯狂,尤其是如果您开发大型企业应用程序。这就是为什么我们应该考虑依赖注入(inject)和服务方法。我认为这种方法是现代的和 separation of concerns 。在这种情况下,我们将应用程序分为两部分:控制逻辑( Controller 和事件)和参数。

    One kind of parameters would be ordinary “data” parameters. That’s what we pass around functions, manipulate, modify, persist, etc. These are entities, aggregates, collections, case classes. The other kind would be “service” parameters. These are classes which encapsulate business logic, allow communicating with external systems, provide data access.


    这是我的架构的一般工作流程示例。假设我们有一个 UserProfile ,它显示用户的 friend 列表,我们可以选择从 friend 中删除。我在我的 UserServices 类中创建了一个方法,名为:
    - (RACSignal *)removeFriend:(Friend * const)friend
    其中 S 是一个模型/域对象(或者如果它们具有相似的属性,它可以只是一个 FriendsViewController 对象)。底层这个方法解析 FriendsServicesFriend 的 JSON 参数 User , Friend , 0x251353132132313213231323123132我总是将 post-OO 库用于这种样板和我的模型层(前后解析、管理 JSON 中的嵌套对象层次结构等)。解析后,它调用 NSDictionary friend_id 方法来发出实际的 REST 请求,并返回 name 中的 surname 给调用者(无论我们的用户在何种情况下显示 123132 或 4518132 的适当消息)
    如果我们的应用程序是一个非常大的应用程序,我们必须将我们的逻辑分离得更清晰。例如。将“Repository”或模型逻辑与“Service”混合在一起并不*总是*好。当我描述我的方法时,我说过 `removeFriend` 方法应该在 `Service` 层,但是如果我们更迂腐,我们会注意到它更适合属于 `Repository`。让我们记住什么是存储库。 Eric Evans 在他的书 [DDD] 中对其进行了精确的描述:

    A Repository represents all objects of a certain type as a conceptual set. It acts like a collection, except with more elaborate querying capability.


    因此, friend_request_id 本质上是一个外观,它使用集合样式语义(添加、更新、删除)来提供对数据/对象的访问。这就是为什么当你有类似的东西时: APIClient , DELETE , Response 你可以把它放在 RACSignal 中,因为这里的语义非常清晰。代码如下:
    - (RACSignal *)approveFriendRequest:(FriendRequest * const)request;
    绝对是一个业务逻辑,因为它超出了基本的 FriendsViewController 操作并连接了两个域对象( RepositorygetFriendsList ),这就是为什么它应该放在 0x25181314231 层另外我想注意: 不要创建不必要的抽象 。明智地使用所有这些方法。因为如果你用抽象来压倒你的应用程序,这将增加它的意外复杂性,软件系统中的复杂性 Mantle 比其他任何事情都重要
    我向您描述了一个“旧的”Objective-C 示例,但是这种方法可以很容易地适应 Swift 语言并进行更多改进,因为它具有更多有用的特性和功能糖。我强烈建议使用这个库: causes more problems 。它允许您创建一个更优雅的 getUserGroups 层(您记得是我们的主力)。现在我们的 removeFriend 提供程序将是一个值类型(枚举),其扩展符合协议(protocol)并利用解构模式匹配。 Swift 枚举 + 模式匹配允许我们像经典函数式编程一样创建 Moya。我们的微服务将像通常的 Objective-C 方法一样使用这个改进的 Repository 提供程序。对于模型层而不是 CRUD,您可以使用 algebraic data types 或者我喜欢使用更优雅和功能更强大的 ObjectMapper library 库。
    所以,我描述了我的通用架构方法,我认为它可以适用于任何应用程序。当然,可以有更多的改进。我建议你学习函数式编程,因为你可以从中受益很多,但也不要走得太远。消除过多的、共享的、全局可变状态、创建 Argo 或创建没有外部副作用的纯函数通常是一种很好的做法,新的 Friend 语言鼓励这样做。但永远记住,用大量的纯函数模式、类别理论方法重载你的代码是一个坏主意,因为其他开发人员会阅读和支持你的代码,他们可能会对 Request 和你的代码中的这类东西感到沮丧或害怕。不可变模型。与 Service 相同的事情:不要 APIClient 你的代码 immutable domain model ,因为它可以很快变得不可读,特别是对于新手。当它可以真正简化您的目标和逻辑时使用它。
    所以,多阅读、混合、试验,并尝试从不同的架构方法中挑选出最好的。这是我能给你的最好的建议。

    关于ios - 构建 iOS 网络应用程序(REST 客户端)的最佳架构方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24162051/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com