Swift使用協議加泛型程式設計(一)

ezbuy_Metal團隊發表於2019-01-28

最近重構公司內部使用的PDT應用. 其中有一個模組使用了協議加泛型重構了一下, 感覺不錯.

原本程式碼有4份非常類似的.

import Foundationopen class DeliveryRegionController: NSObject { 
internal(set) public var regions: [Region] = [] public var selected: Region? private let loader: ((@escaping ([Region]) ->
Void, @escaping ErrorHandler) ->
Bool)? init(loader: @escaping (@escaping ([Region]) ->
Void, @escaping ErrorHandler) ->
Bool) {
self.loader = loader super.init()
} @discardableResult public func reload(completion: @escaping (DeliveryRegionController) ->
Void, failure: @escaping ErrorHandler) ->
Bool {
if let loader = self.loader {
return loader({
regions in self.regions = regions completion(self)
}, failure)
} else {
completion(self) return false
}
}
}複製程式碼
import Foundationopen class DeliveryStationController: NSObject { 
internal(set) public var stations: [Station] = [] public var selected: Station? private let loader: ((@escaping ([Station]) ->
Void, @escaping ErrorHandler) ->
Bool)? init(loader: @escaping (@escaping ([Station]) ->
Void, @escaping ErrorHandler) ->
Bool) {
self.loader = loader super.init()
} @discardableResult public func reload(completion: @escaping (DeliveryStationController) ->
Void, failure: @escaping ErrorHandler) ->
Bool {
if let loader = self.loader {
return loader({
stations in self.stations = stations completion(self)
}, failure)
} else {
completion(self) return false
}
}
}複製程式碼

貼了兩份, 可以發現除了資料型別, 其他的幾乎一模一樣. 為了提高程式碼的複用性, 於是重構一下.

最近有個朋友一直問我Swift協議相關的東西, 於是, 上面這些程式碼, 我就用了協議加泛型來重構一下, 剛好還可以給她講解一下.

首先根據以上程式碼, 定義一份協議. 返回的型別定義了一個泛型Object.

public protocol ObjectLoader { 
associatedtype Object var objects: [Object] {
get
} var selected: Object? {
get set
} var loader: ((@escaping ([Object]) ->
Void, @escaping ErrorHandler) ->
Bool)? {
get
} func reload(completion: @escaping (Self) ->
Void, failure: @escaping ErrorHandler) ->
Bool
}複製程式碼

這裡reload方法裡面返回了Self, 這樣下面遵循這個協議的類, 就只能用final了, 如果不想用final, 就要稍微麻煩一些, 這裡就不作討論了.

好了, 下面來實現具體的類.

public final class ObjectLoaderController<
Object>
: NSObject, ObjectLoader
{
fileprivate var _objects: [Object] = [] fileprivate var _selected: Object? internal(set) open var objects: [Object] {
get {
return self._objects
} set {
self._objects = newValue
}
} open var selected: Object? {
get {
return self._selected
} set {
self._selected = newValue
}
} open let loader: ((@escaping ([Object]) ->
Void, @escaping ErrorHandler) ->
Bool)? init(loader: @escaping (@escaping ([Object]) ->
Void, @escaping ErrorHandler) ->
Bool) {
self.loader = loader super.init()
} @discardableResult public func reload(completion: @escaping (ObjectLoaderController) ->
Void, failure: @escaping ErrorHandler) ->
Bool {
if let loader = self.loader {
return loader({
objects in self._objects = objects completion(self)
}, failure)
} else {
completion(self) return false
}
}
}複製程式碼

這樣重構完了以後, 使用就是用這個Loader就好了, 返回的型別在定義好了以後, Swift的自動推導型別可以很方便的獲取到具體型別.

因為資料的load我也使用了協議, 並且專案相關的原因, make這個loader的方法就像下面這樣.

public protocol DeliveryRegionLoader: class { 
func loadDeliveryRegion(completion: @escaping ([TRNameIdPair]) ->
Void, failure: @escaping ErrorHandler) ->
Bool
}extension DeliveryRegionLoader where Self: EZFilter {
public func makeDeliveryRegionController() ->
ObjectLoaderController<
Region>
? {
guard self.filterOptions.contains(.region) else {
return nil
} let controller = ObjectLoaderController {
[weak self]completion, failure ->
Bool in return self?.loadDeliveryRegion(completion: {
result in completion(result.map {
Region(pair: $0)
})
}, failure: failure) ?? false
} return controller
}
}複製程式碼

具體的load方法, 也是一個類遵循了上面DeliveryRegionLoader協議, 然後實現具體的方法.

extension NHFilter: DeliveryRegionLoader { 
func loadDeliveryRegion(completion: @escaping ([TRNameIdPair]) ->
Void, failure: @escaping ErrorHandler) ->
Bool {
DeliveryService.UserGetRegions(deliveryTypeId: self.deliveryMethod.id, success: {
(result) in completion(result)
}, failure: failure) return true
}
}複製程式碼

以上, 就是傳說中的協議加泛型程式設計了, 其實並沒有那麼神祕…

來源:https://juejin.im/post/5c4e6d0e51882525dc62fc53

相關文章