尝试在iOS应用上安装“干净的架构”

dddzy1tm  于 2023-02-26  发布在  iOS
关注(0)|答案(2)|浏览(174)

最近我一直在重新思考我的android architecture project,试图使它适应一个更“干净的架构”,特别是suggested by “Uncle Bob”的设计类型。
它涉及到几层抽象、很好的责任隔离和通过依赖注入实现的非常强的依赖反转;这最终导致了一个非常解耦的便携式系统。一个通过单元测试和集成测试进行测试的完美候选者。
在我的Android实现中,我最终有三个不同的模块或层:
-域名:实体、交互器、表示器**(纯java模块)**
-data:(作为存储库向域提供数据)(android库模块)
-介绍:用户界面相关材料、片段、活动、视图等**(安卓应用程序模块)**
所以,我一直在努力寻找iOS生态系统的最佳解决方案,我尝试过创建一个有多个目标的项目来实现同一个解决方案:
-域名命令行目标(看起来很奇怪,但我认为这是最纯粹的swift目标)
-数据可可接触框架
-介绍可可触摸框架
通过这种方法,我可以像使用android模块那样使用这些目标,但我发现的第一个警告是,我需要手动添加每个新文件到依赖目标。
但是我对多目标项目的了解非常有限,我的意思是我从来没有创建过多目标的iOS应用程序,所以我不知道解决方案是否会使用一个框架(可可touch/cocoa)作为目标,而不是域层的命令行模块。
任何想法都将不胜感激。
谢谢!

nukf8bse

nukf8bse1#

Uncle Bob的Clean Architecture绝对适用于iOS、Swift和Obj-C。架构是语言不可知论者。Uncle Bob自己主要用Java编写代码,但在他的演讲中很少提到Java。他所有的幻灯片甚至没有显示任何代码。这是一种适用于任何项目的架构。
为什么我这么肯定?因为我已经学习MVC、MVVM、ReactiveCocoa和Clean Architecture两年了。到目前为止,我最喜欢Clean Architecture。我通过将7个苹果示例项目转换为使用Clean Architecture来测试它。我专门使用这种方法一年多了。每次都效果更好。
其中一些好处是:

  • 更快更轻松地查找和修复错误。
  • 将业务逻辑从视图控制器提取到交互器中。
  • 将表示逻辑从视图控制器提取到表示器中。
  • 使用快速且可维护的单元测试自信地更改现有行为。
  • 编写具有单一责任的较短方法。
  • 用明确的边界分离类依赖关系。

我们还添加了一个路由器组件,这样我们就可以使用多个故事板。没有更多的冲突。
编写单元测试也大大简化了,因为我只需要测试边界上的方法,而不需要测试私有方法。最重要的是,我甚至不需要任何模拟框架,因为编写自己的模拟和存根变得微不足道。
我在Clean Swift上写了我过去两年研究iOS架构的经验,我还整理了一些Xcode模板来生成所有的清洁架构组件,以节省大量的时间。

    • UPDATE**-在下面的注解中回答@Víctor Albertos关于依赖注入的问题。

这是一个非常好的问题,需要一个很长很详细的答案。
始终牢记VIP循环。在这种情况下,doSomethingOnLoad()方法不是一个 * boundary * 方法。相反,它是一个仅在CreateOrderViewController中调用的 * internal * 方法。在单元测试中,我们测试单元的预期行为。我们给出输入,观察输出,然后将输出与预期进行比较。
是的,我本可以让doSomethingOnLoad()成为一个私有方法。但是我选择了不这样做。Swift的目标之一就是让开发人员编写代码变得更容易。所有的边界方法都已经在 * input * 和 * output * 协议中列出了。真的没有必要在类中添加额外的私有修饰符。
现在,我们确实需要以某种方式测试"CreateOrderViewController应该在加载此请求数据时执行某些操作"的行为,对吗?如果doSomethingOnLoad()是私有方法,我们不能调用它,我们怎么测试呢?你调用viewDidLoad()viewDidLoad()方法是边界方法。哪个边界?用户和视图控制器之间的边界!用户对设备做了一些操作,使其加载另一个屏幕。那么我们如何调用viewDidLoad()呢?您可以这样做:

let bundle = NSBundle(forClass: self.dynamicType)
let storyboard = UIStoryboard(name: "Main", bundle: bundle)
let createOrderViewController = storyboard.instantiateViewControllerWithIdentifier("CreateOrderViewController") as! CreateOrderViewController
let view = createOrderViewController.view

简单地调用createOrderViewController.view属性会导致viewDidLoad()被调用,很久以前我从某人那里学到了这个技巧,但是Natasha The Robot最近也提到了它。
当我们决定测试什么时,只测试边界方法是非常重要的。如果我们测试一个类的每一个方法,测试就会变得极其脆弱。我们对代码所做的每一个修改都会破坏很多很多测试。很多人因此而放弃。
或者,可以这样想,当你问如何模仿CreateOrderRequest时,首先问doSomethingOnLoad()是否是一个边界方法,你应该为它编写test。如果不是,那是什么?在这个例子中,边界方法实际上是viewDidLoad()。输入是"当这个视图加载时"。输出是"用这个请求对象调用这个方法"。
这是使用CleanSwift的另一个好处。所有的边界方法都列在文件的顶部,分别使用明确命名的协议CreateOrderViewControllerInputCreateOrderViewControllerOutput。你不需要去别处找!
想一想如果你测试doSomethingOnLoad()会发生什么。你模拟请求对象,然后Assert它等于你期望的请求对象。你在模拟某个东西并比较它。它就像assert(1, 1)而不是var a=1; assert(a, 1)。有什么意义呢?太测试。太脆弱
现在,你需要模拟CreateOrderRequest。在你验证视图控制器组件可以生成正确的CreateOrderRequest之后,当你测试CreateOrderInteractordoSomething()边界方法时,你可以使用接口依赖注入来模拟CreateOrderRequest
简而言之,单元测试不是测试类的每个单元,而是将类作为一个单元进行测试。
这是一种心态转变。
希望能有所帮助!
我有3个系列的草稿文章在WordPress上不同的主题:
1.深入了解Clean Swift的每个组件
1.如何将复杂的业务逻辑分解为工作者和服务对象。
1.在Clean Swift iOS架构中编写测试
你想先听哪一个?我应该增加测试的系列吗?

8ljdwjyq

8ljdwjyq2#

在我看来,清洁架构是一套想法,规则,原则...使代码更好。
Android Clean Architecture(https://stackoverflow.com/a/61126623/4770877)
通过这种方法,我可以像使用android模块那样使用这些目标。
您可以创建目标About(https://stackoverflow.com/a/59215808/4770877)(应用程序目标或框架目标...),但这取决于您的需要
如果你读费尔南多·塞哈斯的《Architecting Android...Reloaded
您可能已经看到我使用Android模块来表示架构中涉及的每一层。
讨论中反复出现的一个问题是:为什么?答案很简单...错误的技术决定
其思想是,不必使用某些构建组件来实现Clean Architecture

相关问题