简单的3步,重构我们的代码

栏目: IOS · 发布时间: 7年前

内容简介:许多年前,小梁进了他的第一家公司,不久迎来了他的第一个项目,他翻了下苹果的文档决定用URLSession来调后台API,于是他在每个需要和服务器交互的地方写下了如下代码:经过几期迭代,产品找到小梁同学说:“把我们项目所有的网络请求超时时间设成30s,并在所有的请求头里添加指定参数”。 小梁同学可以对每一个第一步,封装

许多年前,小梁进了他的第一家公司,不久迎来了他的第一个项目,他翻了下苹果的文档决定用URLSession来调后台API,于是他在每个需要和服务器交互的地方写下了如下代码:

class AViewController: UIViewController {
    func loadData() {
        let url: URL = "https://api.com/path?query=key"
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let error = error {
                self.failure(error)
            } else {
                self.success(data ?? Data())
            }
        }
        task.resume()
    }

    func failure(_ error: Error)  {

    }

    func success(_ data: Data) {

    }
}
复制代码

经过几期迭代,产品找到小梁同学说:“把我们项目所有的网络请求超时时间设成30s,并在所有的请求头里添加指定参数”。 小梁同学可以对每一个 loadData() 函数进行修改,但是他现在已经编码一年,可以说得上是一个有些许经验的 程序员 了,于是他决定对项目的网络请求部分进行重构。

第一步,封装 DataLoader 类来集中管理网络请求:

class DataLoader {
    enum Result {
        case success(Data)
        case failure(Error)
    }

    func load(_ url: URL, completion: @escaping (Result) -> Void ) {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 30
        configuration.httpAdditionalHeaders =  [
            "Accept-Encoding": "acceptEncoding",
            "Accept-Language": "acceptLanguage",
            "User-Agent": "userAgent"
        ]
        let session = URLSession(configuration: configuration)
        let task = session.dataTask(with: url) { (data, response, error) in
            if let error = error {
                return completion(.failure(error))
            }

            completion(.success(data ?? Data()))
        }

        task.resume()
    }
}

class AViewController: UIViewController {
    func loadData() {
        let url: URL = "https://api.com/path?query=key"

        DataLoader().load(url) { (result) in
            switch result {
            case .failure(let error):
                self.failure(error)
            case .success(let data):
                self.success(data)
            }
        }
    }

    func failure(_ error: Error)  {

    }

    func success(_ data: Data) {

    }
}
复制代码

到这里,其实已经可以满足了产品提的需求。但是小梁毕竟想表现的更“老鸟”一点,而且也想让代码更具有'swif style'于是便进行了提取协议

第二步,提取协议,这一步的目的是把请求部件移到一个协议中。代码如下

protocol NetworkEngine {
    typealias Handler = (Data?, URLResponse?, Error?) -> Void

    func request(for url: URL, completion: @escaping Handler)
}

extension URLSession: NetworkEngine {
    typealias Handler = NetworkEngine.Handler

    func request(for url: URL, completion: @escaping Handler) {
        let task = dataTask(with: url, completionHandler: completion)
        task.resume()
    }

}

复制代码

如您所见, URLSession 遵守 NetworkEngine 协议,并封装了请求细节。这样,我们就可以专注于NetworkEngineAPI。

第三步,依赖项注入,现在,让我们 DataLoader 从之前更新我们使用新的 NetworkEngine 协议,并将其作为依赖项注入。我们将使用 URLSession.shared 默认参数,以便我们可以保持向后兼容性和以前一样的便利性。代码如下

class DataLoader {
    enum Result: Equatable {
        case success(Data)
        case failure(Error)
    }

    static var defaultEngine: URLSession = {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 30
        configuration.httpAdditionalHeaders =  [
            "Accept-Encoding": "acceptEncoding",
            "Accept-Language": "acceptLanguage",
            "User-Agent": "userAgent"
        ]
        let session = URLSession(configuration: configuration)
        return session
    }()

    private let engine: NetworkEngine

    init(engine: NetworkEngine = defaultEngine) {
        self.engine = engine
    }

    func load(_ url: URL, completion: @escaping (Result) -> Void ) {
        engine.request(for: url) { (data, response, error) in
            if let error = error {
                return completion(.failure(error))
            }

            completion(.success(data ?? Data()))
        }
    }
}
复制代码

重构到这里,小梁同学将利用NetworkEngine协议模拟测试,以使他的测试快速,可预测且易于维护。于是他又定义了一个Mock类

class NetworkEngineMock: NetworkEngine {
    typealias Handler = NetworkEngine.Handler

    var requestedURL: URL?

    func request(for url: URL, completion: @escaping Handler) {
        requestedURL = url

        let data = "Hello world".data(using: .utf8)
        completion(data, nil, nil)
    }
}

复制代码

到这里,小梁觉得他功德圆满,既重构了代码,又完成了产品的需求。

####但是,他真的功德圆满吗?


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

暗时间

暗时间

刘未鹏 / 电子工业出版社 / 2011-7 / 35.00元

2003年,刘未鹏在杂志上发表了自己的第一篇文章,并开始写博客。最初的博客较短,也较琐碎,并夹杂着一些翻译的文章。后来渐渐开始有了一些自己的心得和看法。总体上在这8年里,作者平均每个月写1篇博客或更少,但从未停止。 刘未鹏说—— 写博客这件事情给我最大的体会就是,一件事情如果你能够坚持做8年,那么不管效率和频率多低,最终总能取得一些很可观的收益。而另一个体会就是,一件事情只要你坚持得足......一起来看看 《暗时间》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

MD5 加密
MD5 加密

MD5 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具