最近重構公司內部使用的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
}
}複製程式碼
以上, 就是傳說中的協議加泛型程式設計了, 其實並沒有那麼神祕…