我对 VIPER 架构中协议的好处有点困惑。我了解 DI(依赖注入)是通过协议实现的,并有助于避免对象之间的直接依赖关系——我同意。
但是我正在从使用的角度来看一个真正的好处,一个例子可能是 - 特别是协议如何帮助在单元测试中受益(测试交互器部分)。
我们不能通过方法回调的 using 块来实现相同的目标吗?希望有人可以通过一些示例从使用角度帮助我理解
干杯
我对 VIPER 架构中协议的好处有点困惑。我了解 DI(依赖注入)是通过协议实现的,并有助于避免对象之间的直接依赖关系——我同意。
但是我正在从使用的角度来看一个真正的好处,一个例子可能是 - 特别是协议如何帮助在单元测试中受益(测试交互器部分)。
我们不能通过方法回调的 using 块来实现相同的目标吗?希望有人可以通过一些示例从使用角度帮助我理解
干杯
使用回调,例如从 Interactor 到 Presenter,会使测试 Presenter 变得更加困难。
在为 Presenter 如何处理输入(从 Interactor 发送)编写测试时,您的测试必须在 Presenter 上调用某些方法,这会导致 Presenter 对 Interactor 进行调用,这会导致 Interactor 将数据发送到 Presenter .
通过让 Presenter 实现由 Interactor 定义的协议,您的测试可以直接在 Presenter 上调用适当的输入方法。
至于声明协议,我以模拟角色而不是对象的方式练习 TDD(http://www.jmock.org/oopsla2004.pdf)。这些协议通过关注对象做什么(它的角色)而不是它如何做来帮助提供更好的抽象。
协议本身对单元测试没有什么价值。您的单元测试将为被测系统的依赖项提供测试替身(http://martinfowler.com/bliki/TestDouble.html)。即使您将依赖项公开为具体类,您仍然可以为您的测试创建测试替身。
在 Objective-C 中,您可以使用模拟库,例如 OCMock ( http://ocmock.org ) 或 OCMockito ( https://github.com/jonreid/OCMockito ) 来创建存根、间谍或模拟具体类。
在 Swift 中,您可以通过子类化每个用作依赖项的具体类来创建测试替身。
简而言之,协议不是用来简化单元测试的,而是在更高的抽象层次上描述应用程序的功能。
下面是一个例子,说明抽象协议在事后是如何有益的:
我创建了一个协议来表示用户可以在屏幕上执行的操作,例如ProfileUserActions
,具有changeName
和等操作changeAddress
。Presenter 实现ProfileUserActions
了 ,并且 View 接受了 aProfileUserActions
作为依赖项。当用户点击屏幕上的按钮时,视图将向其userActions
对象发送适当的消息。
当我想添加分析时,我能够创建一个新的、独立的ProfileAnalytics
类,该类也实现了ProfileUserActions
. 我在 View 和 Presenter 之间插入了分析对象,这允许应用程序捕获分析,而无需修改 View 或 Presenter。
通过使用协议,您可以更轻松地更换 VIPER 结构中的实现。例如,您可能有一个交互器正在处理一个正在写入文件系统的类。您不想在单元测试中测试文件系统,因此,如果您将文件系统写入操作放在协议后面的交互器中,则可以将文件系统写入操作替换为内存实现。
至于交互器本身的协议,我认为对您的选择更加务实一点是值得的。如果为测试构建交互器很容易,并且作为测试的一部分不会引起任何副作用,那么可能不需要该协议。另一方面,如果您必须创建许多其他依赖项,那么让交互器符合协议可能是值得的,这样您就可以更轻松地伪造从交互器获得的输出。