美文网首页
[IOS架构]面向协议编程POP

[IOS架构]面向协议编程POP

作者: 沈枫_ShenF | 来源:发表于2020-09-21 13:08 被阅读0次

    swift中灵活使用协议可以帮助我们编写干净的代码,而且协议还有以下好处。

    可测试

    public enum Error: Swift.Error {
     case connectivity
     case invalidData
    }
    protocol Service {
     func fetchData(_ callback: @escaping ([Data]?, Error?) -> ())
    }
    class DataService: Service {
     func fetchData(_ callback: @escaping ([Data]?, Error?) -> ()) {
      // fetch data
     }
    }
    

    现在,怎么去测试这个类呢,当然肯定不会点击去真正的请求URL并等待响应,然后断言结果。相反,我们只需要创建一个模拟类来测试:

    class ServiceTest: XCTestCase {
     func test_noData_returnsNoData() {
      let sut = ServiceSpy(data: nil, error: Error.connectivity)
      
      sut.fetchData { (data, error) in
       XCTAssertNil(data)
       XCTAssertNotNil(error)
      }
     }
     func test_withData_returnsData() {
      let sut = ServiceSpy(data: Data(base64Encoded: “test_data”), error: nil)
      sut.fetchData { (data, error) in
       XCTAssertNotNil(data)
       XCTAssertNil(error)
      })
     }
     class ServiceSpy: Service {
      var data: Data?
      var error: Error?
      init(data: Data?, error: Error?) {
       self.data = data
       self.error = error
      }
      func fetchData(_ callback: @escaping ([Data]?, Error?) -> ()) {
       if let data = data {
        callback(data, nil)
       }
       if let error = error {
        callback(nil, error)
       }
      }
     }
    }
    

    这样做有利于增加代码的覆盖率,提高可测试性。

    可实现多重继承

    protocol Talkable {
     func talk()
    }
    extension Talkable {
     func talk() {
      print(“Person can Talk”)
     }
    }
    protocol Walkable {
     func walk()
    }
    extension Walkable {
     func walk() {
      print(“Person can Walk”)
     }
    }
    class Person: Talkable, Walkable {
    }
    let person = Person()
    person.talk()
    person.walk()
    

    依赖注入

    依赖注入优点:

    • 单一职责原则
    • 可测试性
    • 避免单例实例的突变
      等等...

    依赖注入要求我们传入的参数遵循协议,这样做的好处是:

    • 我们可以在外部注入一个通用类,只要它遵循协议。
    • 也可以注入一个可测试的模拟类,便于测试。
    protocol HelloViewModelable {
     func fetchHello()
    }
    protocol HelloViewControllerable {
     func updateView(hello: String)
    }
    class HelloViewModel: HelloViewModelable {
     weak var delegate: HelloViewControllerable?
     func fetchHello() {
      delegate?.updateView(hello: “Hello”)
     }
    }
    class HelloViewController: UIViewController, HelloViewControllerable {
     let viewModel: HelloViewModelable
     @IBOutlet weak var helloLabel: UILabel!
     init(viewModel: HelloViewModelable) {
      self.viewModel = viewModel
      self.viewModel.delegate = self
     }
     override func viewDidLoad() {
      super.viewDidLoad()
      viewModel.fetchHello()
     }
     func updateView(hello: String) {
      DispatchQueue.main.async { [weak self] in
       self?.helloLabel.text = “\(hello)”
      }
     }
    }
    

    这里我们在控制器中注入了一个HelloViewModel,实际上我们也可以注入一个HelloViewModel的变体,当然,也可以注入一个可测试的模拟viewModel。

    复用

    通过协议扩展,我们可以在实现相同功能的多个类中重用一段代码。

    protocol Breathable {
     func breathe()
    }
    extension Breathable {
     func breathe() {
      print(“This creature can breathe.”)
     }
    }
    struct Fish: Breathable {
    }
    struct Bird: Breathable {
    }
    struct Elephant: Breathable {
    }
    let fish = Fish()
    let bird = Bird()
    let elephant = Elephant()
    fish.breathe()
    bird.breathe()
    elephant.breathe()
    

    相关文章

      网友评论

          本文标题:[IOS架构]面向协议编程POP

          本文链接:https://www.haomeiwen.com/subject/ebfczctx.html