美文网首页
[swift 进阶]读书笔记-第九章:泛型 C9P3 使用泛型

[swift 进阶]读书笔记-第九章:泛型 C9P3 使用泛型

作者: liaoworkinn | 来源:发表于2019-04-13 16:41 被阅读0次

    第九章:泛型 Generics

    9.3 使用泛型进行代码设计 Designing with Generics

    本小节就比较实用了,对网络请求以及数据转模型的实际场景使用了泛型的二次封装
    这样的编程思想还是很酷的,我们也可以在自己的项目中推广一下

    普通时候我们的网络请求已经数据转模型的代码
        func loadUsers(callback: ([User]?) -> ()) {
            ///获取数据
            let usersURL = webserviceURL.appendingPathComponent("/users") let data = try? Data(contentsOf: usersURL)
            ///数据转模型
            let json = data.flatMap {
            try? JSONSerialization.jsonObject(with: $0, options: []) }
            let users = (json as? [Any]).flatMap { jsonObject in jsonObject.flatMap(User.init)
            }
            ///回调
            callback(users) 
        }
    

    需要注意的是

    1️⃣网络请求可能失败

    2️⃣JSON解析可能失败

    3️⃣数据转模型可能失败
    我们都需要返回nil, 对回调使用flapMap来排除nil的情况

    提取共通功能

    上面的Demo是对[User]模型的网络请求, 如果我们需要请求订单量[Bill]的网络请求,又需要写一个新的方法重复写很多代码。
    这样不可取
    我们可以把函数中的User提取出来, URLString作为参数传入来封装一个泛型方法 loadResource, 声明为A的泛型

        func loadResource<A>(at path: String, parse: (Any) -> A?, callback: (A?) -> ())
        {
            let resourceURL = webserviceURL.appendingPathComponent(path) let data = try? Data(contentsOf: resourceURL)
            let json = data.flatMap {
            try? JSONSerialization.jsonObject(with: $0, options: []) }
            callback(json.flatMap(parse)) 
        }
    

    封装完毕之后我们对请求[User]的网络请求就可以这样写了

        func loadUsers(callback: ([User]?) -> ()) {
            loadResource(at: "/users", parse: jsonArray(User.init), callback: callback)
        }
    

    上面用了一个辅助函数,jsonArray 主要作用是: 它首先尝试将一个 Any 转换为一个 Any 的数组,接着 对每个元素用提供的解析函数进行解析,如果期间任何一步发生了错误,则返回 nil:

        func jsonArray<A>(_ transform: @escaping (Any) -> A?) -> (Any) -> [A]?  
        {
             return { array in
                guard let array = array as? [Any] else { return nil
            }
            return array.flatMap(transform) }
        }
    

    二次优化--创建泛型数据类型

    上面的loadResource函数中 path 和 parse 耦合非常紧 , 一旦path改变了 parse也会一起改变。
    我们可以封装成一个结构体,来描述要加载的资源

    struct Resource<A> { 
        let path: String
        let parse: (Any) -> A?
    }
    

    接下来 我们可以给 我们已经封装好的结构体 Resource 结构体来定义一个extension 让Resource 直接调用网络请求数据转模型的方法

    extension Resource {
        func loadSynchronously(callback: (A?) -> ()) {
            let resourceURL = webserviceURL.appendingPathComponent(path) let data = try? Data(contentsOf: resourceURL)
            let json = data.flatMap {
            try? JSONSerialization.jsonObject(with: $0, options: []) }
            callback(json.flatMap(parse)) }
    }
    

    最终我们封装好的战果😎~

    ///网络请求一个用户模型数组就变的如此方便。
    let usersResource: Resource<[User]> = Resource(path: "/users", parse:jsonArray(User.init))
    userResourse.loadSynchronously = { userModels in  
        ///我取到了我想要的 userModels 模型数组啦~~
    }
    
    感悟

    这一小节是真的有用,对于我们实际开发及代码风格有很大的启发。 下次代码优化的时候可以试试这样的封装思维~。

    文章源文件地址,大家如果有更好的想法和观点欢迎交流

    相关文章

      网友评论

          本文标题:[swift 进阶]读书笔记-第九章:泛型 C9P3 使用泛型

          本文链接:https://www.haomeiwen.com/subject/ofglwqtx.html