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

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

内容简介:许多年前,小梁进了他的第一家公司,不久迎来了他的第一个项目,他翻了下苹果的文档决定用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)
    }
}

复制代码

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

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


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

查看所有标签

猜你喜欢:

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

Mastering Regular Expressions, Second Edition

Mastering Regular Expressions, Second Edition

Jeffrey E F Friedl / O'Reilly Media / 2002-07-15 / USD 39.95

Regular expressions are an extremely powerful tool for manipulating text and data. They have spread like wildfire in recent years, now offered as standard features in Perl, Java, VB.NET and C# (and an......一起来看看 《Mastering Regular Expressions, Second Edition》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

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

HEX HSV 互换工具