![](https://img.haomeiwen.com/i1477238/64bb9c4a480216c7.jpg)
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()
网友评论