【问题标题】:iOS VIPER architecture, who has to instantiate a whole module?iOS VIPER 架构,谁必须实例化整个模块?
【发布时间】:2017-04-11 10:12:11
【问题描述】:

考虑到VIPER结构

我有两个模块,A 和 B。第一个模块 A,通过演示者,想要执行一个必须在模块 B 中完成的动作,所以告诉它的线框去做。问题是,谁负责实例化整个模块(视图、交互器、演示者......)。我看到了一些不同方法的例子:

  • 在应用的开头创建所有模块。
  • 在模块的线框中创建整个模块,所以本例中BWireframe的一个类方法实例化了所有的B模块。

考虑到线框负责路由,它是否也负责创建其模块?

【问题讨论】:

  • 一般来说我更喜欢 Wireframe 来创建它的模块,如果我们在开始时创建所有模块然后如果有很多模块那么它没有意义,所以我同意第二种方法更好。跨度>

标签: ios architecture


【解决方案1】:

TL;DR:我建议您应该使用像 Typhoon 这样的 DI 框架并让它处理实例化。

您可能不希望您的线框实例化 VIPER 模块(View、Presenter、Interactor、DataManager)中的所有内容的原因是您将直接创建对每个组件的依赖关系。

依赖关系使软件无法更改:如果我们考虑到它与我们的 onion architecture / hexagonal architecture 帽子,线框将跨越至少两个单独的洋葱层的边界,因为不仅知道视图,但数据管理器。

这迫使我们将线框视为一个通用基础设施类:即,位于应用程序最外层的东西。

但是,这与线框的另一个(也是更实际的)职责相矛盾:进行导航。虽然这仍然是基础设施层,但它绝对属于UIKit-presentViewController: 等)。

所以恕我直言,VIPER 的线框做得太多了。

唯一明智的做法是将两个职责分开:

  1. VIPER 类的初始化:您可以引入factory,或使用专为解决此问题而设计的 DI 工具
  2. 进行导航:这在Wireframe 的职权范围内。

补充说明

如果您想知道“ 实例化了下一个模块?”,那么恕我直言,我认为某些 Module A 的线框与 Module B 的线框对话是不对的尽管VIPER post 说了什么。

原因是Module A 现在将依赖于Module B,导致紧密耦合:这违背了模块的目的。在VIPER-TODO project at GitHub =]

上有更多关于这个话题的讨论

希望这会有所帮助。

【讨论】:

  • 谢谢fatuhoku,这很有启发性。在我的项目中,我有四个模块,对此我有些担心。我下一步将在下一个版本中使用 Typhoon :) 再次感谢您。
  • 我第一次从这篇文章中了解到台风……我的生活现在好 1000 倍。我欠你一杯啤酒@fatuhoku
  • @SleepsOnNewspapers 哈哈。很高兴我能帮上忙=]
  • 我只是想记录一下使用 Typhoon 的费用。我将它集成到我的项目中,我发现它在将我的代码重构为单元时很有帮助。然而,它也增加了我的编译时间(通过 CocoaPods)。我遇到的另一个问题是,当我想用​​ Swift 编写一些程序集时,我意识到由于语法问题,Typhoon 无法通过其协议注入对象。请参阅github.com/appsquickly/Typhoon/issues/418 如果您使用按协议注入来实现 VIPER,那么需要注意这一点。
【解决方案2】:

我认为模块的入口是线框,所以我同意你的第二种方法。

创建一个模块包括创建它的所有对象,在我看来这有点浪费。

【讨论】:

    【解决方案3】:

    在自己的路由器上拥有模块初始化代码将消除一堆代码重复,特别是对于大型项目。 您需要创建一次这些扩展:

    // ReusableView.swift
    protocol ReusableView: class {}
    
    extension ReusableView {
        static var reuseIdentifier: String {
            return String(describing: self)
        }
    }
    
    
    // UIViewController.swift
    extension UIViewController: ReusableView { }
    
    
    // UIStoryboard.swift
    extension UIStoryboard {
        func instantiateViewController() -> T where T: ReusableView {
            return instantiateViewController(withIdentifier: T.reuseIdentifier) as! T
        }
    }

    然后,在每个VIPER模块的路由器上留下初始化代码:

    // MainSearchRouter.swift
    class MainSearchRouter {
    
        // MARK: Properties
        weak var view: UIViewController?
    
        // MARK: Static methods
        static func setupModule() -> MainSearchViewController {
            let viewController = UIStoryboard(name: MainSearchViewController.storyboardName, bundle: nil).instantiateViewController() as MainSearchViewController
            let presenter = MainSearchPresenter()
            let router = MainSearchRouter()
            let interactor = MainSearchInteractor()
    
            viewController.presenter =  presenter
    
            presenter.view = viewController
            presenter.router = router
            presenter.interactor = interactor
    
            router.view = viewController
    
            interactor.output = presenter
    
            return viewController
        }
    }

    这可能看起来有很多步骤,但好消息是:前面提到的插件也可以自动完成!

    【讨论】:

    • 谢谢马塞洛,我会看看这个,听起来很有趣!最后我以同样的方式解决了这个问题,我使用了一个名为ModuleFactory 的协议来实例化整个模块。你可以在这里看到一个例子:github.com/emenegro/space-cells-ios
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 2019-08-21
    • 1970-01-01
    • 1970-01-01
    • 2018-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多