【问题标题】:Trying to fit a "clean architecture" on an iOS app试图在 iOS 应用程序上安装“干净的架构”
【发布时间】:2015-11-19 00:27:59
【问题描述】:

最近我一直在重新考虑我的android architecture project,试图将其调整为更“干净的架构”,特别是suggested by “Uncle Bob” 的那种设计。

它涉及多层抽象,良好的职责隔离和依赖注入实现的非常强的依赖倒置;这最终导致了一个非常解耦的便携系统。通过单元测试和集成测试进行测试的完美候选人。

在我的 android 实现中,我最终拥有了三个不同的模块或层:

-:实体、交互者、演示者(纯java模块)

-数据:(作为存储库向域提供数据)(android 库模块)

-presentation:与ui相关的东西、片段、活动、视图等(android应用模块)

所以,我正在尝试找出 iOS 生态系统中的最佳方法。 我尝试创建一个具有多个目标的项目来实现相同的解决方案:

-domain命令行目标(这看起来很奇怪,但我认为这是最纯粹的 swift 目标)

-数据可可触控框架

-演示文稿可可触控框架

通过这种方法,我可以像使用 android 模块一样使用这些目标。但我发现的第一个警告是我需要手动将每个新文件添加到依赖目标。

但我的知识在具有多个目标的项目中非常有限。我的意思是我从未创建过具有多个目标的 iOS 应用程序。所以我不知道解决方案是否会使用框架(cocoa touch/cocoa)作为目标,而不是域层的命令行模块。

任何想法都会非常感激。

谢谢!

【问题讨论】:

  • 看看 MVVM 和 VIPER 模式和方法 (google)。我认为您的 Bob 叔叔方法不适用于 Obj-c。 MVVM linkVIPER link祝你好运
  • 感谢您提供的信息,但您根本没有回答我的问题。我问过如何使用某种目标来隔离纯 swift 代码。你可以详细说明为什么你认为不可能实现那种架构,因为我很确定它是,不管程序语言(objective-c,swift ...)
  • 我实际上并没有看到任何问题——“所以,我正在尝试找出 iOS 生态系统中最好的方法”我不知道(或喜欢) 迅速,所以我无法帮助你。也许迅速标记您的问题,您可能会得到一些快速的开发人员。你有没有看过毒蛇,因为如果你费心去看,你会发现这种模式与你鲍勃叔叔的模式非常匹配——这就是我喜欢帮助别人的原因,他们会因为你不回答他们没有问的问题而责备你。
  • 当我读到“我认为你的 Bob 叔叔方法不适用于 Obj-c”时。我停止阅读。你为什么这么说,同时你提供的资源显然已经实施了这种方法。无论如何,谢谢你的帮助,真的。但是实现 viper 示例的人……他们并不关心为每个抽象层创建不同的目标。

标签: android ios architecture


【解决方案1】:

Uncle Bob 的整洁架构绝对适用于 iOS、Swift 和 Obj-C。建筑与语言无关。 Bob 叔叔本人主要使用 Java 编写代码,但在他的谈话中他很少提及 Java。他所有的幻灯片甚至都没有显示任何代码。它是一种适用于任何项目的架构。

为什么我这么肯定?因为我学习了 2 年的 MVC、MVVM、ReactiveCocoa 和 Clean Architecture。到目前为止,我最喜欢 Clean Architecture。我通过将 7 个 Apple 示例项目转换为使用 Clean Architecture 对其进行了测试。我已经专门使用这种方法一年多了。每次都效果更好。

其中一些好处是:

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

我们还添加了一个路由器组件,以便我们可以使用多个故事板。没有更多的冲突。

编写单元测试也大大简化了,因为我只需要在边界处测试方法。我不需要测试私有方法。最重要的是,我什至不需要任何模拟框架,因为编写自己的模拟和存根变得微不足道。

我在Clean Swift 写了我过去 2 年学习 iOS 架构的经验,我还整理了一些 Xcode 模板来生成所有 Clean Architecture 组件以节省大量时间。

更新 - 在下面的评论中回答 @Víctor Albertos 关于依赖注入的问题。

这是一个非常棒的问题,需要很长的详细答案。

始终牢记VIP 周期。在这种情况下,doSomethingOnLoad() 方法不是 boundary 方法。相反,它是一个internal 方法,仅在CreateOrderViewController 内调用。在单元测试中,我们测试一个单元的预期行为。我们提供输入,观察输出,然后将输出与我们的期望进行比较。

是的,我可以将doSomethingOnLoad() 设为私有方法。但我选择不这样做。 Swift 的目标之一是让开发人员更容易编写代码。 inputoutput 协议中已经列出了所有边界方法。真的没有必要在课堂上乱扔无关的私有修饰符。

现在,我们确实需要以某种方式测试“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() 是否是您应该为其编写测试的边界方法。如果不是,那是什么?在这种情况下,边界方法实际上是viewDidLoad()。输入是“当这个视图加载时”。输出是“用这个请求对象调用这个方法。”

这是使用 Clean Swift 的另一个好处。所有边界方法都列在文件顶部的显式命名协议CreateOrderViewControllerInputCreateOrderViewControllerOutput 下。您无需寻找其他地方!

想想如果你要测试doSomethingOnLoad() 会发生什么。您模拟请求对象,然后断言它等于您预期的请求对象。你在嘲笑和比较它。就像assert(1, 1) 而不是var a=1; assert(a, 1)。重点是什么? 太多测试。太脆弱

现在,您可以模拟CreateOrderRequest。在您验证正确的CreateOrderRequest 可以由视图控制器组件生成之后。当您测试CreateOrderInteractordoSomething() 边界方法时,然后使用接口依赖注入模拟CreateOrderRequest

简而言之,单元测试并不是要测试一个类的每个单元。它是关于将类作为一个单元进行测试。

这是一种思维方式的转变。

希望有帮助!

我在 Wordpress 中有 3 个关于不同主题的系列草稿:

  1. 深入了解每个 Clean Swift 组件
  2. 如何将复杂的业务逻辑分解为工作对象和服务对象。
  3. 在 Clean Swift iOS 架构中编写测试

您最想先听听以下哪一个?我应该增加测试系列吗?

【讨论】:

  • 我会读你的帖子,因为它看起来很有趣。但我还不知道什么样的目标可能是领域层。因为在 Android 生态系统上,我创建了一个纯模块 java - 对 Android 没有任何依赖,因此可以将其移植到桌面应用程序,但使用 Xcode 我只能在 cocoa (OSX) 或 cocoa touch(iOS) 之间进行选择.那是对的吗?我无法创建一个可以在 OSX 和 iOS 系统上编译的通用框架。
  • 有什么理由不使用依赖注入?您的所有依赖项都被实例化,而不是通过构造函数/方法传递/注入到特定类。它不会让单元测试变得有点困难吗?
  • 您完全可以为 iOS 和 OS X 构建一个通用框架。我为 DevBase 做了这个。如果您有兴趣,我已经写了post 来展示如何做到这一点。但它适用于旧版本的 Xcode。最新的 Xcode 和 Swift 已经支持框架。
  • 你能告诉我你在问依赖注入的具体类和方法吗?
  • 创建订单视图控制器。第 40 行。您正在实例化一个 CreateOrderRequest。你不能嘲笑它。所以,我不知道你如何对这个类进行单元测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-25
  • 2019-04-08
  • 1970-01-01
  • 1970-01-01
  • 2016-08-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多