内容简介:Swift中,函数是一等公民问题最近因为某个类中有重复代码,在“固化思维”重构之后,虽然原来的重复代码去掉了,但又有如下样式的代码,仔细想想,其实还是有重复,如files和dirs的获取,以及对结果的处理,代码是完全一样的。
Swift中,函数是一等公民
问题
最近因为某个类中有重复代码,在“固化思维”重构之后,虽然原来的重复代码去掉了,但又有如下样式的代码,仔细想想,其实还是有重复,如files和dirs的获取,以及对结果的处理,代码是完全一样的。
extension Array where Element: WeiyunItem { fileprivate func restore(dir: WeiyunDir?) -> Completable { let files = compactMap { $0 as? WeiyunFile } let dirs = compactMap { $0 as? WeiyunDir } return Completable.create { observer -> Disposable in WeiyunSDK.sharedInstance()?.restoreRecycleFile(files, dir: dirs, pdirkey: dir?.dirkey, ppdirkey: dir?.pdirkey, block: { _, _, err in if case let error as NSError = err { observer(.error(error)) } else { observer(.completed) } }) return Disposables.create() } } fileprivate func delete() -> Completable { let files = compactMap { $0 as? WeiyunFile } let dirs = compactMap { $0 as? WeiyunDir } return Completable.create { observer -> Disposable in WeiyunSDK.sharedInstance()?.clearRecycleFile(files, dir: dirs, block: { _, _, err in if case let error as NSError = err, !ignoreError(error) { observer(.error(error)) } else { observer(.completed) } }) return Disposables.create() } } }
解决
按传统的思路来写的话,就是将相同的代码抽取到函数里,然后再调用相应函数来避免重复代码。 重构后大概的代码如下:
extension Array where Element: WeiyunItem { private func splitItems() -> ([WeiyunFile], [WeiyunDir]) { let files = compactMap { $0 as? WeiyunFile } let dirs = compactMap { $0 as? WeiyunDir } return (files, dirs) } private func processResult(observer: PrimitiveSequenceType.CompletableObserver, err: Error?) { if case let error as NSError = err { observer(.error(error)) } else { observer(.completed) } } fileprivate func restore(dir: WeiyunDir?) -> Completable { let tuple = splitItems() return Completable.create { observer -> Disposable in WeiyunSDK.sharedInstance()?.restoreRecycleFile(tuple.0, dir: tuple.1, pdirkey: dir?.dirkey, ppdirkey: dir?.pdirkey, block: { _, _, err in self.processResult(observer: observer, err: err) }) return Disposables.create() } } fileprivate func delete() -> Completable { let tuple = splitItems() return Completable.create { observer -> Disposable in WeiyunSDK.sharedInstance()?.clearRecycleFile(tuple.0, dir: tuple.1, block: { _, _, err in self.processResult(observer: observer, err: err) }) return Disposables.create() } } }
看起来还ok,除了Completable.create及Disposables.create()之外,基本没有重复代码了。
继续思考
不过,这是终点了吗?并不是,毕竟还有部分代码是重复的。
我们现在换一种思路来思考,第一张截图里面,除了调用的WeiyunSDK的接口不同,传入的参数不同,其它所有代码都是一样的,那么是否可以在这儿做文章?
再回到开头看下这句话:Swift中函数是一等公民。这句话的意义是说函数也可以被操作、变换、处理等,你想到的基本都能做。
那么,把函数作为值传入处理函数中,在处理函数中调用处理就ok。通过传入不同的函数,即可实现调用不同的请求。
但有个很大的问题,函数类型不一样,restoreRecycleFile多了第2、3两个参数!
如何把restoreRecycleFile和clearRecycleFile变为具有相同参数的函数,就是要解决的问题。
今天的主角: 柯里化 ,就是来解决这个问题的。
柯里化
柯里化是一个通用的概念,在函数式编程里面非常重要。它在维基上的定义是:
把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
就作用来说,柯里化可以改变函数类型,可以提前绑定其中的参数。
Github上也有一些现成的柯里化开源库,可以直接用的。如 Curry 、 Prelude 等
可以看如何将两个参数变一个参数的简单实现,以及如何使用柯里化:
public func curry(_ function: @escaping ((A, B)) -> C) -> (A) -> (B) -> C {
return { (a: A) -> (B) -> C in { (b: B) -> C in function((a, b)) } }
}
// 自定义函数
func myAdd(a: Int, b: Int) {
return a + b
}
let f = curry(myAdd)(5) // 这样就可以变为只接受一个参数的函数,
f(10) // 可以这样来调用,并且结果是15
最终方案
在我们这个需求场景中,是需要提前绑定第2和3个参数,并且返回只接受三个参数的函数,这些开源库没有提供相应实现,不自己实现一个并不复杂:
// 可以绑定2、3参数的curry化函数
private func curry2_3(_ function: @escaping (A, B, C, D, E) -> F) -> (C, D) -> (A, B, E) -> F {
return { c, d in { a, b, e in function(a, b, c, d, e) } }
}
然后把这个函数用上之后,就可以将代码整理成如下:
fileprivate extension Array where Element: WeiyunItem { func restore(dir: WeiyunDir?) -> Completable { let f = curry2_3(WeiyunSDK.sharedInstance().restoreRecycleFile)(dir?.dirkey, dir?.pdirkey) return operate(f) } func delete() -> Completable { let f = WeiyunSDK.sharedInstance().clearRecycleFile return operate(f) } private func operate(_ function: @escaping ([WeiyunFile]?, [WeiyunDir]?, RestoreRecycleItemBlock?) -> Void) -> Completable { let files = compactMap { $0 as? WeiyunFile } let dirs = compactMap { $0 as? WeiyunDir } return Completable.create { observer -> Disposable in function(files, dirs, { _, _, err in if case let error as NSError = err { observer(.error(error)) } else { observer(.completed) } }) return Disposables.create() } } }
这样就没有任何重复代码了
这儿只是演示了柯里化非常简单的一种使用场景,在函数式编程中,对函数的处理变换无处不在,柯里化也会大放异彩!
作者:微云iOS团队
链接:https://iweiyun.github.io/2018/09/04/curry-cleancode/
以上所述就是小编给大家介绍的《利用柯里化去除重复代码》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- iOS去除Xcode代码警告
- 如何去除讨厌的Chrome自动填充黄色背景
- 编译原理学习一,去除代码中的注释
- iOS 去除 Webview 键盘顶部工具栏
- PHP学习笔记–去除数组中的重复数据
- Hive 去除 CSV 字段中的双引号
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。