第九章:泛型 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 模型数组啦~~
}
感悟
这一小节是真的有用,对于我们实际开发及代码风格有很大的启发。 下次代码优化的时候可以试试这样的封装思维~。
网友评论