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

本文原载于我的博客: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图,如下:

img-1

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

3 项目实例

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

img-2

这是一个简单的信息展示类的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) 減輕職責

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值