Skip to content

iOS架构模式 VIPER

BranPeng edited this page Apr 18, 2019 · 1 revision

VIPER是通过单一责任原则进行的, 使用VIPER时遵循此原则去解决问题。

保持一个类单一责任,它使类更强大。
单一责任原则规定,每个模块或类应该对软件提供的功能的单一部分负责,并且该责任应完全由类封装。 它的所有服务都应该与这一责任严格一致。

VIPER的全称是View-Interactor-Presenter-Entity-Router

VIPER 从另一个角度对职责进行了划分,这次划分了 五层。

  • Interactor(交互器) - 包括数据(Entities)或者网络相关的业务逻辑。比如创建新的 entities 或者从服务器上获取数据;要实现这些功能,你可能会用到一些服务和管理(Services and Managers):这些可能会被误以为成是外部依赖东西,但是它们就是 VIPER 的 Interactor 模块。

  • Presenter(展示器) - 包括 UI(but UIKit independent)相关的业务逻辑,可以调用 Interactor 中的方法。

  • Entities(实体) - 纯粹的数据对象。不包括数据访问层,因为这是 Interactor 的职责。

  • Router(路由) - 负责 VIPER 模块之间的转场

如果我们把 VIPER 和 MV(X) 系列做一个对比的话,我们会发现它们在职责划分上面有下面的一些区别:

  • Model(数据交互) 的逻辑被转移到了 Interactor 里面,Entities 只是一个什么都不用做的数据结构体。

  • Controller/Presenter/ViewModel 的职责里面,只有 UI 的展示功能被转移到了 Presenter 里面。Presenter 不具备直接更改数据的能力。

  • VIPER 是第一个把导航的职责单独划分出来的架构模式,负责导航的就是 Router 层。

下面的这个列子并没有涉及到导航和 VIPER 模块间的转场,同样上面 MV(X) 系列架构里面也都没有涉及。

import UIKit

struct Person { // Entity (usually more complex e.g. NSManagedObject)
    let firstName: String
    let lastName: String
}

struct GreetingData { // Transport data structure (not Entity)
    let greeting: String
    let subject: String
}

protocol GreetingProvider {
    func provideGreetingData()
}

protocol GreetingOutput: class {
    func receiveGreetingData(greetingData: GreetingData)
}

class GreetingInteractor : GreetingProvider {
    weak var output: GreetingOutput!

    func provideGreetingData() {
        let person = Person(firstName: "David", lastName: "Blaine") // usually comes from data access layer
        let subject = person.firstName + " " + person.lastName
        let greeting = GreetingData(greeting: "Hello", subject: subject)
        self.output.receiveGreetingData(greeting)
    }
}

protocol GreetingViewEventHandler {
    func didTapShowGreetingButton()
}

protocol GreetingView: class {
    func setGreeting(greeting: String)
}

class GreetingPresenter : GreetingOutput, GreetingViewEventHandler {
    weak var view: GreetingView!
    var greetingProvider: GreetingProvider!

    func didTapShowGreetingButton() {
        self.greetingProvider.provideGreetingData()
    }

    func receiveGreetingData(greetingData: GreetingData) {
        let greeting = greetingData.greeting + " " + greetingData.subject
        self.view.setGreeting(greeting)
    }
}

class GreetingViewController : UIViewController, GreetingView {
    var eventHandler: GreetingViewEventHandler!
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
    }

    func didTapButton(button: UIButton) {
        self.eventHandler.didTapShowGreetingButton()
    }

    func setGreeting(greeting: String) {
        self.greetingLabel.text = greeting
    }

    // layout code goes here
}
// Assembling of VIPER module, without Router
let view = GreetingViewController()
let presenter = GreetingPresenter()
let interactor = GreetingInteractor()
view.eventHandler = presenter
presenter.view = view
presenter.greetingProvider = interactor
interactor.output = presenter

我们再来评价下它在各方面的表现:

  • 划分 - 毫无疑问的,VIPER 在职责划分方面是做的最好的。

  • 可测性 - 理所当然的,职责划分的越好,测试起来就越容易

  • 易用 - 最后,你可能已经猜到了,上面两点好处都是用维护性的代价换来的。一个小小的任务,可能就需要你为各种类写大量的接口。

OperationQueue

Font

Clone this wiki locally