c# mvvm架构
I’ve been using MVVM-C for quite some time. I’m sure you (probably, I don’t know you) agree that by abstracting the navigation away from the main MVVM architecture code gives you the opportunity for testing. Great!
我已经使用MVVM-C已有一段时间了。 我确定您(也许我不认识您)同意,通过将导航从主要MVVM体系结构代码中抽象出来,可以为您提供测试的机会。 大!
What if you have multiple dependencies? How are these going to be injected into your…wait.. where…what?
如果您有多个依赖项怎么办? 这些将如何注入您的……等待中……在哪里……什么?
Making this clear is the goal of this article.
弄清楚这一点是本文的目标。
Difficulty: Beginner | Easy | Normal | Challenging
难度:初学者| 容易| 普通 | 具有挑战性的
This article has been developed using Xcode 11.4.1, and Swift 5.2.2
本文是使用Xcode 11.4.1和Swift 5.2.2开发的。
先决条件: (Prerequisites:)
You will be expected to be aware how to make a Single View Application in Swift.
您应该知道如何在Swift中制作Single View应用程序 。
Some experience of architecture patterns are required, whether MVC or MVVM.
无论是MVC还是MVVM ,都需要一定的架构模式经验。
The principles of Dependency Injection are explained in this article, but some in-depth explanation always helps!
本文介绍了依赖注入的原理,但是一些深入的说明总是有帮助的!
You’ll need to be comfortable with protocols
您需要熟悉协议
术语: (Terminology:)
Factory Pattern: A design pattern where an object is created without exposing the creation logic to the client. The factory pattern lets a class defer instantiation to subclasses.
工厂模式:一种设计模式,在该模式下创建对象而不将创建逻辑暴露给客户端。 工厂模式允许类将实例化延迟到子类。
问题 (The Problem)
MVVM-C helps with separation of concerns, and allows testing and implementation to be…much better than MVC. Now, nothing is perfect, but it is perfectly possible to have a variety of dependencies which can lead to large and unwieldy initializers.
MVVM-C有助于分离问题,并使测试和实施比MVC更好。 现在,没有什么是完美的,但是完全有可能存在各种依赖关系,这些依赖关系可能导致庞大而笨拙的初始化程序。
One possible solution to this is use of the Factory Pattern, and the use of Dependency Injection to test the relevant classes. This particular article is written with the views programmatically defined, avoiding Storyboards in the entire project, and neatly overriding loadview().
一种可能的解决方案是使用工厂模式,并使用依赖注入来测试相关类。 本文写的是用程序定义的视图,在整个项目中避免使用Storyboard ,并巧妙地覆盖loadview() 。
With that understood, let’s get to it!
了解了这一点,让我们开始吧!
这个例子 (The Example)
The idea of this article is to have a basic skeleton for the MVVM-C architecture. There are few around, particularly coded in Swift and even fewer making an attempt at testing.
本文的想法是为MVVM-C体系结构提供一个基本框架。 周围很少,特别是用Swift编写的代码,甚至很少尝试进行测试。
You’re probably working in a shop where they demand 80% test coverage — and this article and the accompanying repo isn’t quite there (this example isn’t promised to be production ready), however it is a very good start for a developer to build on.
您可能正在一家需要80%测试覆盖率的商店里工作-这篇文章和随附的回购协议还不存在(此示例未保证已准备好投入生产),但是对于开发人员继续发展。
The coordinator has the following responsibilities:
协调员承担以下职责:
- Initialize the network service 初始化网络服务
- Perform network calls 进行网络通话
- Initialize the ViewModel with the fetched data (or instruct the ViewModel to handle a failed network call) 使用获取的数据初始化ViewModel(或指示ViewModel处理失败的网络调用)
实施 (The Implementation)
One of the best ways to explore the implementation is do download the files to your machine and take a look. Give me a nice star when you’re there (if you can).
探索实现的最佳方法之一是将文件下载到您的计算机上并进行查看。 如果可以的话,给我一颗好星星。
I’ve created everything programmatically avoiding storyboards
我已经以编程方式创建了所有内容, 避免使用情节提要
协调人 (The coordinator)
This is at the heart of the matter, because the coordinator controls the flow of the App. If a UIViewController
wishes to move to another UIViewController
it must do so through the coordinator.
这是问题的核心,因为协调器控制着App的流程。 如果一个UIViewController
希望移动到另一个UIViewController
则必须通过协调器来移动。
The main Coordinator is called ProjectCoordinator
that conforms to a RootCoordinator protocol and AbstractCoordinator
protocol. By using protocols
rather than base classes you do end up with a rather more Swifty solution — something recommended for Swift developers.
主要协调器称为ProjectCoordinator
,它符合RootCoordinator协议和AbstractCoordinator
协议。 通过使用protocols
而不是基类,您最终会获得一个更加Swifty的解决方案-建议Swift开发人员使用。
The coordinator protocols
协调器协议
Which are then put to use in out ProjectCoordinator
class.
然后将其在ProjectCoordinator
类中使用。
The ProjectCoordinator concrete class
ProjectCoordinator具体课程
The ProjectCoordinator
has knowledge of just the factory class, which it uses to create the InitialViewController
instance which is then pushed on the UINavigationController
stack. The reference to navigationController
is weak to prevent a retain cycle as the UIViewController
instance has a reference to the ProjectCoordinator
.
ProjectCoordinator
仅了解工厂类,该工厂类用于创建InitialViewController
实例,然后将其推入UINavigationController
堆栈中。 对navigationController
的引用很弱,无法防止保留周期,因为UIViewController
实例对ProjectCoordinator
的引用。
The moveToDetail()
function demonstrates how moving between UIViewController
instances could work, and in fact if we wanted to pass parameters or values from one UIViewController
to another (although questions should be raised about why you’re not doing this through the model) it could be done here.
moveToDetail()
函数演示如何在UIViewController
实例之间移动,以及实际上,如果我们希望将参数或值从一个UIViewController
传递给另一个moveToDetail()
尽管应该提出问题,为什么您不通过模型执行此操作)在这里完成。
该工厂 (The Factory)
The Dependency Factory class lets us inject services. This example has a ‘fake HTTPManager
based upon my basic http manager class at it’s heart, although I’m sure you have your own solution for making network calls (don’t feel like I’m bullying you into this).
Dependency Factory类使我们可以注入服务。 尽管我确定您有自己的用于进行网络调用的解决方案,但此示例的HTTPManager
基于我的基本http管理器类的“伪HTTPManager
”(不要以为我是在欺负您)。
The Factory protocols
工厂协议
For better testing this conforms to my Factory
protocol that enforces conforming classes to have a network manager and the minimum functions in order to create the UIView
instances and UIViewController
instances.
为了更好地进行测试,这符合我的Factory
协议,该协议强制符合标准的类具有网络管理器和最少的功能,以便创建UIView
实例和UIViewController
实例。
The dependency factory concrete class
依赖工厂具体类
The dependency factory creates these artifacts that will be used to create the objects that our App requires.
依赖项工厂将创建这些工件,这些工件将用于创建我们的应用程序所需的对象。
ViewModel类 (The ViewModel classes)
The view models know about the coordinator (but not about the factory). With this design, the view model can hit the coordinator for navigation, and make the transitions happen. Equally the view model has the dependency — the network manager.
视图模型知道协调器(但不知道工厂)。 通过这种设计,视图模型可以命中协调器进行导航,并进行转换。 同样,视图模型具有依赖性-网络管理器。
The view model creates the model from here when the network model returns data from the Endpoint API.
当网络模型从Endpoint API返回数据时,视图模型会从此处创建模型。
The ViewController classes
ViewController类
The UIViewController
classes are responsible for the UIView
classes, and since in this implementation they have no dependencies these are created within the UIViewController
.
UIViewController
类负责UIView
类,并且由于在此实现中它们没有依赖项,因此它们在UIViewController
中创建。
The initializer
初始化器
sometimes choose to let the UIViewController
control navigation, and this means that my UIViewController
classes have visibility of the Coordinator
class.
有时选择让UIViewController
控制导航,这意味着我的UIViewController
类具有Coordinator
类的可见性。
Equally, the view controller has viability of the view model class, and it is the viewmodel classes that are used for network calls in this implementation.
同样,视图控制器具有视图模型类的生存能力,在此实现中,用于网络调用的是视图模型类。
Initialization from the storyboard is not currently supported.
当前不支持从情节提要进行初始化。
View类 (The View classes)
The views are rather dumb here, don’t have any dependencies and just set up their subviews. For the sake of completion, the InitialView
is shown here.
这里的视图很笨,没有任何依赖关系,只是设置了它们的子视图。 为了完整起见,此处显示InitialView
。
模型类 (The Model classes)
The example doesn’t have a particularly inspiring model. At all. I’ve created a model with just a single dataString.
该示例没有特别鼓舞人心的模型。 完全没有 我创建了一个只有单个dataString的模型。
You might well use this to store something like user details (obtained by an API), or whatever. The choice is yours.
您可能会用它来存储诸如用户详细信息(通过API获取)之类的内容。 这是你的选择。
Unfortunately there isn’t much to learn from this class, but for completion’s sake here is the code:
不幸的是,从该课程中学到的东西并不多,但是为了完整起见,下面是代码:
测试中 (Testing)
This article is not about testing specifically, but does contain tests and mocks partly because there is so little out there in existing articles.
本文不是专门针对测试,而是包含测试和模拟,部分原因是现有文章很少。
To keep this article readable (or at least approaching readable) I’ve kept the implementation details of these tests to the repo. To access the code hop over to the repo and press download. You can then read through the files, but below is the explanation of the same.
为了使本文可读(或至少接近可读),我将这些测试的实现细节保留在了仓库中。 要访问代码, 请跳至存储库 ,然后按下载。 然后,您可以通读文件,但以下是对文件的解释。
单元测试 (Unit testing)
ViewModels
视图模型
These would require mocked RootCoordinator
and HTTPManagerProtocol
. No problem, as FactoryMock
and CoordinatorMock
show we can create .
这些将需要RootCoordinator
和HTTPManagerProtocol
。 没问题,正如FactoryMock
和CoordinatorMock
所示,我们可以创建。
UIViews
UIViews
The views should not usually be unit tested. What are you going to check? The positions of the view in a container that doesn’t exist?
视图通常不应进行单元测试。 你要检查什么? 视图在不存在的容器中的位置?
For completeness, if you wanted to do this the code is included in InitialViewTests
.
为了完整InitialViewTests
,如果您要执行此操作,则代码包含在InitialViewTests
。
The coordinator
协调人
We can mark a test function with throws, and any exception would cause the test to fail. This can be used in conjunction with a guard
statement a mock for UINavigationController
to ensure the correct View Controller is pushed onto the UINavigationController
(well, a mock of UINavigationController
) stack.
我们可以用throw标记测试功能,任何异常都会导致测试失败。 这可以与guard
语句结合使用,该guard
语句是UINavigationController
的模拟,以确保将正确的View Controller推送到UINavigationController
(当然,是UINavigationController
的模拟)堆栈。
The Factory
该工厂
It doesn’t make any sense to test the dependency factory, as it is this class that is replaced for the tests. FactoryTests
is simply empty.
测试依赖项工厂没有任何意义,因为该类已替换为测试。 FactoryTests
只是空的。
结论 (Conclusion)
I hope this article has helped you out, going through the relevant classes that enable you to create a MVVM-C implementation.
我希望本文能帮助您完成相关的类,使您能够创建MVVM-C实现。
If you’ve been using MVC I appreciate this might seem like a change and, yes, well it is.
如果您一直在使用MVC,我希望这似乎是一种更改,是的,确实如此。
However having different methods of producing your App will only help in the long run, and I hope that this article has really helped you out.
但是,从长远来看,采用不同的方法来生产App只会有所帮助,我希望本文对您有所帮助。
Thanks for reading!
谢谢阅读!
If you’ve any questions, comments or suggestions please hit me up on Twitter
如果您有任何疑问,意见或建议,请在Twitter上打我
翻译自: https://medium.com/@stevenpcurtis.sc/mvvm-c-architecture-with-dependency-injection-testing-3b7197eb2e4d
c# mvvm架构