Tag: 软件构架

使用Swift的iOS上的MVVM架构模式

MVVM ( 模型视图ViewModel )是用于在编程代码中分离各层的高级体系结构设计模式之一。 它是由Microsoft架构师Ken Cooper和Ted Peters发明的,用于简化UI中使用的事件驱动的编程,它是Microsoft WPF和Silverlight子系统的一部分。 除了MVVM模式外,还有另外2种是众所周知的,它们是MVC ( 模型视图控制器 )和MVP ( 模型视图演示器 )。 在本文中,我将仅描述MVVM。 我将在此处解释与此模式有关的所有必需的详细信息。 另外,我还将在这里以实际示例为例,说明为什么值得在您的iOS应用中使用它以及如何使用它。 愉快的阅读! MVVM将代码分为3个高级层 。 从最低层到最高层看,它们如下: 最低的Model层,负责处理应用程序的领域模型,这些模型可以直接传递到更高的ViewModel层。 除了域模型定义之外,该层还提供存储,更新,删除和获取高层使用的数据的服务。 中间的ViewModel层负责使用较低的Model层提供的数据处理应用的业务逻辑,并对来自较高View层的用户交互进行逻辑处理。 最高的View层负责根据ViewModel的业务逻辑呈现适当的应用程序UI,并在那里处理Model的数据,还负责用户的交互,以捕获并传递它们以供较低ViewModel层处理。 在iOS代码中,当每个较大的组件的基类是UIViewController和UIView时 ,这些类将充当View层。 在下面,您可以找到UML组件图,其中显示了具有相互依赖性的所有MVVM层。 答案很简单,将负责模型,业务逻辑和视图的3个基本层分开,这些基本层始终是每个iOS应用程序的一部分。 并且使它们彼此独立,不紧密耦合,而是相互补充。 使用MVVM模式准备它们使它们也可以在多个地方重用,并且可以独立测试,从而影响更好的代码稳定性和质量。 由于所有代码看起来干净,易读且令人愉悦,因此正好可以轻松地找到不需要集中所有不重要细节的内容。 对于软件架构师和软件工程师而言,最重要的是代码的组织合理,这就是我们所有人都应该追求的! 假设我们想实现一个非常简单的应用程序,使用户能够基于2个不同的搜索引擎来搜索人们的个人公共数据,假设它是Facebook和LinkedIn。 从UI角度来看,这些用户将能够选择上述引擎之一,以输入一些文本进行搜索,然后单击搜索按钮,然后将搜索结果返回给UI。 下面是一个示例性的iOS Swift代码原型,基于以上描述介绍了MVVM模式的用法。 模型组件 从以上所有描述中可以看到,但主要是在呈现的代码和UML图中,当您希望对iOS Swift代码进行分层时,MVVM体系结构设计模式似乎非常有用,尤其是在标题为“为什么要使用MVVM?”。 这就是为什么如果您关心此处提到的要求,并且希望您的代码满足这些要求,那么您绝对应该考虑在您的应用程序中使用这种方法。 我只想补充并强调,这不是实现这些目标的唯一方法,还有更多。 因此,我建议您熟悉其余的现有模式(至少是MVC,MVP,但也可以尝试VIPER作为iOS应用程序中非常流行的模式),然后选择最适合您和您的目的的模式。 您还应该尝试试验现有方法并进行修改。 也许您会发现一些值得关注和使用的新东西。 或者,也许您会发现到目前为止尚不存在的,通用且可重复使用的东西,这不仅是您自己的完美选择,也是我们成千上万的软件设计师和软件工程师的最佳选择。 祝您好运并成功搜索!

软件构架:MVC设计模式

软件体系结构是关于做出基本的结构选择,一旦实施,更改成本很高。 每个软件开发项目都经历多个阶段:概念,设计,开发,测试等等。 作为一名初级开发人员,对我而言显而易见的是,在开始编写任何代码之前,至关重要的是,全面规划应用程序的体系结构基础,以确保您的应用程序从一开始就具有模块化,稳定和可扩展的特性。 架构设计有助于在项目中尽早发现潜在问题,以便有机会在施工开始之前进行更改。 有效的架构模式可提高代码的可读性,因为它们需要考虑直到以后才变得可见的问题。 什么是建筑设计模式? 设计模式是针对软件设计中常见问题的可重用解决方案。 它们是旨在帮助我们编写易于理解,易于测试和易于重用的代码的模板。 设计模式有助于创建松耦合的代码,以便以后可以在代码中轻松更改或替换其组件。 使用设计模式构建应用程序确实可以为您带来回报。 开发人员非常了解我们的应用程序会发生变化:您可能想添加新功能,可能需要修复一些错误,您的雇主可能会要求您在视图控制器上快速更改配色方案,或者您可能需要更新您的代码以与新版本的iOS软件或新设备保持兼容。 架构设计模式是使您的应用程序能够应对这些更改的准则。 现在我们已经回答了“ 为什么? 让我们来看看“ 如何? ” iOS设计模式: iOS开发最常见的设计模式是: MVC , MPV , MVVN和Viper 。 我将仅介绍MVC和MVVN,因为这些是我目前最感兴趣的设计模式。 在此博客中,我将重点关注MVC,接下来是关于MVVN的后续博客。 什么是MVC? 模型视图控制器(MVC)由Trygve Reenskaug于1979年发明。它是iOS开发中最常见的面向对象设计模式。 MVC可以非常清楚地分离应用程序的数据-逻辑,视图和控制器。 MVC根据对象的一般角色对它们进行分类,并鼓励根据每个角色对代码进行清晰的分离。 每个对象都属于以下组之一: 该模型负责数据和逻辑:诸如持久性,模型对象,解析器和网络代码之类的内容都存放在这里。 视图负责负责模型的可视化表示的对象以及用户可以与之交互的控件; 想任何以“ UI”前缀开头的东西。 Controller充当应用程序的视图对象及其模型对象之间的中介。 控制器编排查看事件和模型更改。 现在,我们了解了MVC如何管理这三个阵营之间的通信,我们的工作是根据每个对象所扮演的角色来明确区分代码。 遵守MVC设计模式需要不断关注对象的位置,以避免潜在的陷阱。 以下是一些需要牢记的准则: 控制器可以与之对话并了解有关该模型的所有信息。 控制器的工作是从模型中获取所需信息以显示给用户。 控制器还可以通过我们在控制器中创建的插座直接与视图对话。 视图只是控制器的奴才 -保持视图简单! 模特和模特 永远都不要说话。 MVC的缺点: 设计模式没有万灵药。 在MVC中,视图和控制器紧密耦合。 iOS视图控制器类通常将同时包含UI逻辑和数据逻辑,这将使一个逻辑的修改影响另一个逻辑-创建MVC的一种“ M / […]

警卫声明被低估

前几天,当我与一个朋友谈论函数式编程时,我意识到我们经常只喜欢讨论性感的东西,而对其他简单的东西没有给予足够的重视。 在Swift中, guard声明是我认为功能非常强大但没有得到编程社区足够重视的小功能之一。 Guard不仅是if !condition语句的语法糖。 考虑以下代码。 如果!loading { 加载() } 如果当前状态未加载,它将加载。 但是我们可以更好地编写如下: 如果正在加载{return} load() 当前状态加载时返回。 它更干净,我们能够从花括号中取出load() 。 并且请注意,上面的if语句不只是if 。 如果是特殊的,用于提早返回。 它应该拥有自己的语法。 从版本2开始,Swift为此提供了guard声明。 警卫!loading else {return} load() 确保当前状态未加载,否则返回。 如果错过了return ,编译器将抱怨。 与使用if语句的版本相比,它更容易理解,因为它使用特殊的条件语法。 我们马上就知道那条线是干什么的。 警卫队要早日返回。 通常,我们将它们放在函数的顶部。 但是, guard声明并不公平,因为对我们大多数人来说,这只是一个条件声明。 但是我认为, guard应该得到更多的荣誉。 考虑以下代码。 守护!loading else {return} 警卫!渲染其他{返回} 卫队个人资料已加载其他{返回} 警卫文章已加载其他{返回} 警卫评论加载其他(返回) 守卫likesLoaded else {return} render() 以上代码在控制器中非常常见。 控制器通常是最复杂,最难阅读的。 想象一下:当控制器从服务器加载数据时,可能会发生很多事情,例如,推送通知到达,按下后退按钮,按下主屏幕按钮进入背景,设备旋转,无意间轻按了随机按钮,网络中断等等。 所有这些都应该得到妥善管理,这非常困难。 这就是为什么控制器非常丑陋的原因。 但是guard声明使它更令人恶心! […]

我让我的iOS项目陷入混乱。

不久前,我休了几个星期的假期。 作为首席开发人员,我对团队充满信心,并知道所有团队成员都是经验丰富的开发人员,并且对项目本身有丰富的知识。 他们中的大多数人比我拥有更多的语言经验,并且所有人都在使用TDD进行工作。 该项目掌握得很好。 欢迎回来! 我同样充满信心地回来了,很高兴看到项目经理和产品负责人对团队所做的工作感到满意。 团队很高兴地告诉我,一切进展顺利,他们提供了所有预期的功能,甚至开始从事一些期待已久的技术任务。 愿景很简单: 文件无处不在 。 我打开了这个项目,意识到了以前错过的一切,为什么我的头脑不明白发生了什么。 该项目的组织无处不在。 名称难以理解的文件夹,来自项目层次结构中同一级别上不同层的对象文件。 对于这个项目的新手来说,根本不可能理解该看哪里。 我开始了解发生了什么,但还不确定为什么会发生。 直到我问了一个关于两个特定类的角色的问题,这两个类应该是同一类的,得到的答案是: 好吧,是的,该类以这种方式工作,但是另一类则根本不以这种方式工作。 我相信,即使您有时在性能上有所损失,但优势仍然太大,以至于您无法在域元素周围阐明模块。 人脑只能弥补很多东西,而当功能实际上被分为多个模块时,很容易就看不到您实际在做什么。 您的项目应始终反映您所做的设计决策,并且应明确您的工作内容。 如果您喜欢这篇文章并认为其他人可能会感兴趣,请随时分享并给予一些鼓掌。 我计划写更多文章,所以 如果您想了解下一个内容,请在这里 关注我 。 您也可以在 Twitter 上 关注 我,讨论任何事情。

“嵌套” iOS应用程序体系结构

在深入探讨之前,首先让我给您一些有关“嵌套”的信息。 无论团队位于单个组织内还是独立公司,Nested都是一个团队到团队的交流平台。 我不会向您解释嵌套的更多内容,因为它本身就是文章。 我大约在一年前加入团队,当时该项目处于Beta测试阶段,并且正在进行一些重组。 服务器正在从PHP迁移到GoLang,许多场景和模型将被完全更改和重构。 我以Nested作为一名Android开发人员开始工作,当时的android应用存在很多问题,尽管其中只有少数对用户可见且明智。 主要问题很简单,结构的设计根本没有灵活性。 我的祖先不是一些新手开发人员,但项目定义以及开发人员在一年中已经更改了两次。 但是最重​​要的是缺少文档。 我将该应用程序维护了一个月,但是由于新版本完全不同(甚至用户界面设计也被完全更改),所以我几乎从头开始开发该应用程序。 Nested存在一个大问题,这使其难以开发和维护:作为使用WebSocket与Server进行通信的实时应用程序。 因此,我要做的第一件事就是开发一个平台来像其他常见的HTTP库一样发送请求并获得其响应。 我选择Volley的原因仅仅是因为它一直是(现在仍然是)我最喜欢的用于通过HTTP与服务器通信的库,所以我为WebSocket开发了完全相同的平台。 但这仅仅是开始,发送请求和获得响应并不是Nested的大问题,但是应用程序应该在收到来自服务器的特定同步信号后进行自我更新。 就是在我向项目“ SyncManager”中添加新零件时。 2个月后,Android版本获得了稳定性,由于找不到可靠的iOS开发人员,我接受了自己开发iOS应用程序的挑战。 当然,我从文档开始。 用Java编写大约6年后,[我爱JAVA! [不要与我讨论!] Swift是一种安全的语言,很容易引起我的注意。 从Android迁移到iOS并不是一件容易的事,但是我决定忘记有关Android的所有知识,而像一个没有任何先入之见的全新平台一样面对iOS。 我开始研究设计模式和体系结构,并根据Nested的实时环境选择了经过细微改动的简单MVC,因为我们应该尽快开发它,三个月后,第一个生产版本已经发布。发布,现在是我保留的时间,可以在项目变得太大而无法更改之前考虑早日进行重组。 我当时在阅读有关VIPER的信息,但对我们来说这还不够方便,然后我设计了自己的产品。 下图是我们的架构概览: 现在让我们开始描述图中的每个实体: 视图控制器: 正如其本身所说,View Controller是一个简单的视图控制器,类似于iOS中的默认UIViewController,但具有更多的功能和参数。 我在项目中将其称为“ BaseViewController” ,几乎所有的视图控制器都扩展了该类。 我添加的一些参数如下: isVisible:布尔; 此变量实际上与“ viewWillAppear”和“ viewDidDisappear”这两个本地函数一起使用 hasUpdate:布尔; 该变量指示控制器在不可见时是否接收到任何更新信号。 subscriptiondSyncs:[Int]; 是控制器在“ SyncManager”中预订的同步类型的列表,例如: [SyncManager.POSTS_SYNC,SyncManager.COMMENTS_SYNC] 。 (再次查看该图) missedUpdates:[String :(类型:Int,fieldId:String,数据:Any?)]; 这是一个字典,如果该控制器当前不可见,则该字典保存该类型的最后一个更新对象,例如,如果您收到了一个Post的两个更新,则只有包含最新数据的最后一个更新对象才会被保存。存储。 与该体系结构相关的额外功能是: syncData(type:Int,fieldId:String,data:Any?); 由于在解释了参数之后该函数的实现已经很清楚了,所以我只复制代码: func syncData(type:Int,fieldId:String,data:Any?){ .. if!isVisible […]

MVVM和VIPER之间的界线模糊

最初发表在 Swift Post上 。 如果您开发移动应用程序已有一段时间,那么您可能听说过MVVM和VIPER。 虽然有些人说MVVM不能很好地扩展,但另一些人则说VIPER太过分了。 我在这里解释为什么我认为它们非常相似,我们甚至不需要将两者分开。 首先让我们快速了解一下MVVM和VIPER。 View提供用户操作以查看模型 。 视图模型处理这些操作并更新其状态。 然后, 视图模型通过数据绑定或使用委派或块手动通知视图 。 视图将用户操作传递给演示者 。 然后, 演示者将这些动作传递给交互器或路由器 。 如果该动作需要计算,则交互器将其处理并将状态返回给presenter 。 演示者然后将此状态转换为演示数据并更新视图 。 导航逻辑封装在路由器中 , 演示者负责触发它。 有关这些架构的更多信息,请参考 Bohdan Orlov的 这篇精彩文章 : iOS Architecture Patterns 我们的首要目标是将UI和业务逻辑分开。 因此,我们可以轻松更新UI而不破坏任何业务规则,也可以隔离测试业务逻辑。 MVVM和VIPER都保证了这一点,但是方式不同。 如果从这个角度来看,它们的结构如下。 MVVM在UI层中只有一个视图组件,而VIPER将UI职责分为三个组件,即View,Presenter和Router。 同样很明显,业务层看起来几乎相同。 让我们看看它们在实践中在UI层中实际上有何不同。 想象一下,我们正在使用MVVM构建一个简单的应用程序,该应用程序将从IMDB中获取前25部电影并将其显示在列表中。 这些组件可能如下所示。 几周后,苹果发布了iOS 26,乔尼·艾夫(Jony Ive)通过引入全新的设计系统,拉开了一个新招。 我们的设计师兴奋不已,并迅速提出了适用于iOS的新UI。 现在,我们的工作将是实施此新设计并通过A / B测试证明其可用性,这意味着我们将仅向一定比例的用户显示新UI。 实际上,我们的信誉很好! 我们只需要为iOS创建一个符合MovieListView协议的新视图,并将其与演示者连接即可。 十分简单。 protocol MovieListView: […]

MVC-RS

RS字母在汽车工业中经常用来指代给定汽车的最强大版本。 对于MVC-RS,适用相同的想法:它是MVC,但是由于另外两个额外的层(路由器和存储),功能更加强大。 香草MVC MVC的第一个版本是在70年代与SmallTalk一起推出的,旨在满足GUI应用程序不断增长的需求。 苹果公司推荐的用于构建应用程序的MVC新版本略有不同。 在此版本中,视图层和模型层不再相互了解。 完全分离这2个层的主要优点是使它们可重复使用。 但是,具有可重复使用的层意味着什么? 可重用性 Swift可以在所有Apple平台,iOS,macOS,watchOS和tvOS上重用,但也可以在Linux上以及某些天在Windows和android上重用。 重用View层与重用一些通用代码不同,因为View与它们所运行的平台紧密耦合。 这是有道理的,您没有呈现相同类型的UI,也不允许在手表,手机,平板电脑,计算机或电视上进行相同类型的用户交互。 如果您在macOS上编写了View层,则使用了AppKit,因此,只有在AppKit可用的情况下,该层才可以重用,这意味着只能在macOS上使用。 Model层没有View这样的约束,因此您应该能够在所有支持该语言的平台上重用Models。 您在iOS开发中经常遇到的第一个常见错误是在模型中使用UIKit。 Apple并没有为您提供帮助,因为他们在文档和某些WWDC会话中都犯了同样的错误。 这样做的问题是您不再可以在其他平台上重用Model。 让我们看一下这个例子: 在上面的示例中,您有3个不同的数据库提供程序。 如果您使用的是db A,则可以在任何地方重用Model。 但是,您可能还在macOS项目上使用了db C,并且该数据库在任何其他平台上均不可用。 在这种情况下,您的模型将无法在其他平台上重复使用。 这里的问题是,当您考虑MVC时,总是只考虑这三个层:模型,视图和控制器。 但实际上,有一个隐式的第四层:持久性(通常是本地文件,本地数据库或远程调用)。 如果您的模型知道您的持久性,则只有在您的持久性可用的情况下它才可以重用。 为避免这种情况,让我向您介绍MVC-RS的S层,它代表存储。 存储 存储层将只在您的持久性和模型之间进行,它将有2个任务: 与持久性交谈 将原始数据从“持久性”转换为模型的实体 与持久性交谈 此任务比看起来要复杂,因为对持久层的调用是异步的,因此它们可能会成功,但也可能会失败。 您的存储层将必须考虑所有这些参数。 转换数据 当您读取应用程序将呈现给用户的数据时,您的存储层会将从持久性获得的原始数据转换为模型的实体。 但是它也应该能够以其他方式做到这一点:当您的应用程序需要保留某些元素时,您的存储层应该知道如何将模型的实体转换为原始数据,并将其发送给Persistence。 重要的是要记住,这两个任务都可能失败。 例 在本文的示例中,您将看到之前的简单Car Model和非常方便且常见的Result枚举模式,而不是繁重的do-try-catch语法来管理错误。 这是您可能在存储层中找到的示例。 读取方法不会立即返回,因为对持久性的调用是异步的。 它们将闭包作为参数,当存储工作完成时将调用它。 如果在与Persistence交谈或将原始数据转换为Model实体时发生故障,则将调用onComplete闭包并显示错误,否则将使用适当的Model实体进行调用。 您可能会发现不同的读取方法,例如一种使用id来获取模型的单个实体的读取方法,或者另一种使用一些参数的读取方法,以防您不想加载模型的每个实例。 编写方法,创建,更新和删除完成了此CRUD示例。 它们具有相同的签名:它们采用Model的实体和onComplete闭包。 如果在将Model实体转换为原始数据或将该原始数据发送到Persistence时失败,则将调用onComplete闭包并显示错误。 否则,将以nil调用。 优点 将存储定义为协议非常方便,因为它允许您针对不同类型的持久性编写不同的实现。 您可能会发现一个用于sqlite数据库的数据库,另一个用于Couchbase数据库的数据库,一个用于某些Rest API调用的数据库……如果您要模拟持久性,还可以编写一个实现用于测试目的。 […]

“经理”对象的麻烦

第一次查看旧项目时,我会扫描警告标志。 我讨论了单例,过多的观察者,今天我将讨论“经理”类。 我必须重申这些是准则,而不是规则,在准则中,这不是关键。 到那里的时候,这里的经理应该对审计进行审核。 但是,如果您有诸如LoginManager,CacheManager和DataManager之类的许多类,则可能会遇到体系结构问题。 症状 管理者可能是职责不明确的症状。 当您考虑时,“经理”一词毫无意义。 在面向对象的编程中,每个类都是一个管理器。 可可触控可能具有UIApplicationManager,UIViewManager甚至是不起眼的NSStringManager。 与现实世界一样,最糟糕的经理人也始于善意。 假设您的AvatarUploadController长超过3,000行,并且其中大多数内容都在处理,例如验证图像尺寸,调整图像大小,上载,检查网络错误等等。 如果您将代码剪切并粘贴到另一个类中,并将其命名为AvatarManager,则它会继续进行。 现在退后一步,问为什么“大规模视图控制器”是个问题。 这是单一责任原则。 每个类都应负责单个功能。 小型视图控制器很好,但是我可以使用大型视图控制器,只要它只能处理视图控制器的内容,例如设置视图层次结构和响应适应性变化。 当您的控制器处理网络时,您就麻烦了。 将大量业务逻辑提取到AvatarManager中可以解决当前的问题。 如果我们要说的是五十行代码,也许就足够了。 但是,如果您要处理成千上万的业务逻辑,则将其包装在Manager中是半措施。 您将扫帚扫入厨房,清理了客厅中的扫帚。 另一方面,管理者可能是人造面向对象设计的征兆。 考虑一下NSFileManager,它实际上只是文件功能的集合。 在真正的OO设计中,可以调用URL实例化NSFile()对象,然后调用其remove()方法,而不是调用removeItemAtURL()。 这并不是说所有代码都必须是面向对象的,而是NSFileManager通过伪造它来破坏功能。 它使用的是singleton defaultManager,因此存在有人(可能是随机的第三方库)分配给其委托属性的风险,并且搞乱了整个应用程序的文件操作。 这真的比一组松散的文件功能要好吗? 如何修复 我已经可以看到初级工程师单击“重构”按钮,准备将Manager与Broker,Coordinator,Utility等进行交换。 这就是规则的问题:詹克找到了方法。 在审核Manager类时,请问:“该类负责什么?”如果您在回答问题时遇到问题,请问:“我可以将其分解吗?”在化身上载示例中,我将提取NetworkAPI ImageValidator ,ImageConverter等。 经理并不是一个大的危险信号,因为即使您知道自己在做什么,它也是一个容易退缩的名词。 如果高级工程师坐在屏幕上想一个名字的时间太长,他们的“你太聪明”的感觉就会浮现。你不想成为那种在不提供任何东西的情况下继续关注美学的业余爱好者。 甚至Apple框架也使用名词,例如Core Location的CLLocationManager。 嘿…让我们来上课吧! 它的职责是什么? 它跟踪用户位置的更改。 它承担太多责任吗? 我不这么认为。 我会担心它是否包含不相关的内容,例如反向地理编码。 好吧,我们可以重命名该类以澄清问题并消除责任感吗? 我将使用CLLocationObserver或CLLocationTracker。 真好! 除了苹果会在游戏后期重命名CLLocationManager之外。 太多的旧代码依赖于此,即使您构建了代码迁移器来提供帮助,也很难证明在向用户交付功能时浪费时间在此上是合理的。 但是,如果您要构建持久的东西或建立任何类型的依赖关系,则值得花额外的几分钟时间在第一时间对事物进行命名。

许多面孔的VIPER-第1部分

我最喜欢的是蓝色。 如此宏伟的生物……。 虽然如此,尽管他是一位伟大的自然爱好者,拥有所有的生物,但我对动物学的了解不足,不会让我进一步感到羞耻。 当然,我将谈论VIPER客户体系结构。 这个故事不是另一个“ iOS上的VIPER简介”或当前其他任何地方。 我相信我们有很多有关该主题的文章。 假定您了解该概念并具有一定的经验。 这也不是要将其与任何其他架构方法或架构进行比较。 我之所以选择它,是因为它确实代表了一种相对较好的“ 清洁体系结构”方法,并且遵循SOLID原则,可以承受。 我什至不会自己使用首字母缩写词,但是我想它使我们所有人都更容易理解该主题。 这个故事是关于以更多样化的方式展示VIPER,并展示其友好的面孔。 为此,我将首先处理一些神话,然后再讨论一些问题,考虑真正的问题,下一次我们将研究一个演示项目,该项目将作为一个可能的解决方案/答案。 误区1:VIPER有很多课程 可以? 您认为应该有几节课? 或更准确地说:您希望在代码中包含多少“ 单一职责原则” ? 也许所有这些都值得商bat。 即使采用SR原则,我们也可以很快就某个特定类的职责进行辩论。 例如,Microsoft在MVVM的定义中将视图模型视为胶水代码 ,作为业务逻辑和视图之间的中间人。 有人可能会争辩说,这种职责范围太广:处理事件,表示逻辑并将模型数据转换为易于查看的形式应视为单独的职责。 关键是:无论采用哪种体系结构,如果要使代码可维护且可靠,那么它将必须符合“干净代码”的许多范式,其中包括SR,小类,尽可能少的耦合,OO模式,全部资金。 ……所有这些都导致“……很多课程……” 。 误解二:僵硬,有很多样板代码 我记得几年前,当我第一次接手VIPER并接管现有项目时,这并不是最好的体验。 不是因为VIPER本身,而是因为与该概念的明显斗争。 我实际上看到了我在这里所说的一个神话: 每个模块都需要很多样板代码 很多时候,这些类都没有创建,只是为了“遵循方法” 开发人员努力地采用需求的逻辑以使其适应VIPER规范,这当然是行不通的,并导致一些严格的遵循并同时破坏它的怪异组合。当我说“破坏”时,我真的这意味着从破坏模块的封装为一的角度来看。 演示者或路由器双向连接太多对象,并使每个结构更改都成为真正的PITA。 我们将在稍后的演示项目中看到,如果您牢记这一点,则不必一定是这样: 您需要遵循的是VIPER所基于的SOLID和其他OOP原则,并据此,您应该创建适合您工作的类。 尽可能在协议扩展,抽象类甚至父类(如果不是太多!!)中实现样板 误区3:学习困难,仅适合高级开发人员 VIPER就像学习SOLID,TDD和OOP设计模式一样困难。 等等…但这还是我们要做的,不是吗? 好的,我也可以添加FRP 。 但是没关系,您的意思是:如果您想成为一名真正的高级开发人员,那么您必须学习上述知识,在这种情况下,您应该编写VIPER编码就像将比萨饼切成小块并撒上手工啤酒一样简单。选择。 是的,但是是初级人员……好吧,那么,如果您的初级同事没有采用VIPER风格,那么您的代码库将采用初级人员的技能组吗? 如果是这样,那么是的,VIPER仅适合高级开发人员。 但是,在我曾经工作过的大多数地方,我们都确保初中生由年长者通过代码进行培训,而并非相反:采用代码以适应最高的通用技能水平…… 误解四:与SDK规定的MVC基础架构配合使用时,效果不佳 我想我在面向模块的体系结构系列中再次揭穿了这一点,特别是在第6部分:超越MVC中。 您也有类似Uber RIB的框架,也可以解决该问题,但是我不得不承认,我个人认为不应将体系结构转变为即时框架解决方案,但这是重点。 误解5:不适合小型项目或原型 同样,在面向模块的体系结构系列中,我们可以看到VIPER类完全精简和小巧,因为大多数样板都在协议扩展中实现。 如果您遵循神话2中所述的方法,那么即使在小型项目和原型中,您也会发现它非常易于使用并且可以快速实现任何种类的特定功能。 […]

VIPER🐍iOS的架构演进

很久以前,我们的iOS团队决定尝试VIPER体系结构模式。 周围的模式有很多变化。 随着时间的流逝,我们有各种各样的用例,而经典的VIPER架构(请参见下文)无法提供标准化的解决方案。 因此,我们根据需要修改了架构模式。 现在,我想分享我们的学习和改进。 对于那些不了解VIPER架构模式的人来说,这是一个简单的示例:假设您要显示应用程序中的项目列表。 通常,您只会创建一个包含该UIView的UIViewController 。 用VIPER方式进行操作意味着您将创建5个组件,这些组件全部由其负责。 视图:我想你们都知道这个组件是什么。 大多数时候,它是一个UIViewController ,有时只是一个UIView 。 它向用户显示UI元素。 演示者:他负责从Interactor中获取数据,从该数据创建视图模型以及更新View。 此外,他告诉路由器导航到另一个视图或显示另一个视图 。 Interactor: Interactor处理业务逻辑,例如从API或本地数据库中获取数据。 路由器:如前所述,他负责导航,这可能意味着显示模态视图控制器或将视图控制器推入导航堆栈。 线框:他连接了上述所有组件,并返回了准备使用的View 。 在下文中,我将使用VIPER堆栈作为上述所有组件的别名。 started我们开始的VIPER架构变体 看看我们开始的VIPER Architecture变体。