欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

利用依赖注入为Controller缩小体积

程序员文章站 2024-02-10 16:09:22
...

本文原载于我的博客:https://www.seekingmini.top/archives/利用依赖注入为controller缩小体积

0 写在前面

MVC模式写着写着,就变成Massive View Controller了…Massive不仅仅是因为Controller部分的代码量巨大,还意味着它与其他部分的联系过于紧密,以致在修改其他部分的代码时会“牵一发而动全身”,大幅增加维护复杂度。那么如何为Cotroller减小耦合度呢?依赖注入是一个好方法。

1 什么是依赖注入

*的解释如下:

在软件工程中,依赖注入(dependency injection)的意思为给予调用方它所需要的事物。 依赖是指可被方法调用的事物。依赖注入形式下,调用方不再直接指使用依赖,取而代之是注入注入是指将依赖传递给调用方的过程。在注入之后,调用方才会调用该依赖。传递依赖给调用方,而不是让调用方直接获得依赖,这个是该设计的根本需求。

上面的解释反正说的云里雾里的…我们在实例中慢慢领悟这个概念。

2 简单示例

通过一个简单的例子,来阐述一下我对依赖注入的理解。首先我们有两个类,分别为OwnerDog类。

// Dog类
class Dog: Pet {
    init() {

    }

    func bark() {
        print("Wooo!")
    }
}

// Owner类
class Owner {
    var pet: Dog

    init() {
        pet = Dog()
    }

    func poke() {
        pet.bark()
    }
}

我们发现,Owner是依赖Dog的,因为Owner类的poke()方法调用了Dog类的bark()方法,这不叫依赖注入。这段代码看似没什么问题,但是如果我修改了Dog类的代码,比如像下面这样:

class Dog: Pet {
    init() {

    }

    // 把bark()改为woof()
    func woof() {
        print("Wooo!")
    }
}

那么在Owner类中我就不得不更改poke()的代码。这说明Owner类和Dog类的耦合度较高。而通过依赖注入,我想达到的效果是:更改Dog类的代码时不用更改Owner类的代码。怎么实现呢?需要利用protocol这个玩意儿。

通过protocol,我们让Dog类遵守Pet这个协议,通过Pet协议定义的方法,来调用Dog类的方法。既保证了通用型,又很好地对Dog类的属性和方法进行了隐藏和封装。Pet协议的定义如下:

protocol Pet {
    func isPoked()
}

Dog类新的定义如下:

class Dog: Pet {
    init() {

    }

    private func woof() {
        print("Wooo!")
    }

    func isPoked() {
        woof()
    }
}

Owner类新的定义如下:

class Owner {
    // pet就是依赖
    var pet: Pet

    init() {
        pet = Dog()
    }

    func poke() {
        // 调用Pet协议定义的方法
        pet.isPoked()
    }
}

将三者画成UML图,如下:

利用依赖注入为Controller缩小体积

我们把Owner类会用到的服务进行抽象化Dog抽象为Pet),再在生成Owner类的实例时,把具体的类别(遵守Pet协议的Dog类)注入给实例,这就是最简单的依赖注入。我们可以看到,改用依赖注入以后,Owner就不知道Dog类了,与它交互的对象是一个遵守了Pet协议的对象。

3 项目实例

接下来,我们通过一个小APP来实践一下依赖注入这种方法。先来看看这个APP的成品:

利用依赖注入为Controller缩小体积

这是一个简单的信息展示类的APP,其中的数据是以JSON格式出现的。在APP载入界面之后,请求API获取数据再展示在界面上。

怎么使用依赖注入呢?我们首先要搞清楚依赖关系。很明显,ViewContrller类依赖获取JSON数据的那个方法或者类,不妨定义为WebService类。换句话说,我们在ViewController类中需要调用WebService类的某一个方法来获取JSON数据,再把数据展示在View上。

搞清楚这个关系以后,我们来定义protocol,把它命名为KivaLoanTableViewControllerDataSource,代码如下:

protocol KivaLoanTableViewControllerDataSource {
    func fetchData(_ completion: @escaping ([Loan]) -> Void)
}

然后让WebService类来遵循这个协议:

class WebService: KivaLoanTableViewControllerDataSource {
    private let url = "https://api.kivaws.org/v1/loans/newest.json"
    
    init() {
        
    }
    
    func fetchData(_ completion: @escaping ([Loan]) -> Void) {
        let decoder = JSONDecoder()
        
        if let url = URL(string: url) {
            let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { (data, _, error) in
                if let error = error {
                    print(error)
                    return
                }
                
                if let data = data {
                    do {
                        let store = try decoder.decode(LoanStore.self, from: data)
                        completion(store.loans)
                    } catch {
                        print(error)
                    }
                }
            }
            task.resume()
        }
    }
}

再在ViewController类中如此调用fetchData(_:@escaping ([Loan])->Void)方法,我们的ViewContrller类命名为KivaLoanTableViewController

class KivaLoanTableViewController: UITableViewController {
    
    var loans: [Loan] = []
    var dataSource: KivaLoanTableViewControllerDataSource?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // fetch data from data source
        dataSource = WebService()
        DispatchQueue.main.async {
            self.dataSource?.fetchData { loans in
                self.loans = loans
                // add an operation
                OperationQueue.main.addOperation {
                    self.tableView.reloadData()
                }
            }
        }
    }
    
    // ...
}

这样写的话,只要保证接口不变,我可以任意修改fetchData(_:@escaping ([Loan])->Void)的代码内容而不用更改KivaLoanTableViewController类的代码,起到了减少耦合性的作用。

具体代码点此下载

参考

Massive View Controller 重構:透過依賴注入 (Dependency Injection) 減輕職責