原文链接:http://www.objc.io/issue-16/functional-swift-apis.html
前言
每每说到API设计,许多常用的模式以及很好实践总是在不断的发展进步。不说别的,我们还有很多很多的栗子从大苹果的Foundation
,Cocoa
,Cocoa Touch
还有其他框架可以举起来。毋庸置疑,这里面还是不够清晰,一直还是有空间去讨论如何让一个API更理想化的契合一个使用案例。尽管如此,普遍的模式对OC开发者来说早已是家常便饭。
随着今天Swift的崛起,API设计这个话题出现得次数比以前更多了。大部分得话题是,我们可以继续保持风格并把原来的处理方式嫁接到Swift上。但是这样做较OC而言完全没有正视Swift中新增添的特性。引用下Swift作者Chris Lattner的话:
Swift dramatically expands the design space through the introduction of generics and functional programming concepts.
这篇文章的目的在于将Swift中的新工具应用到处理提升API设计的问题上。下面都将用Core Image API举栗子。将原有API进行封装后,新的API将更加的安全以及模块化,可以避免一些运行时错误。
目标
目的是整一套API用来让我们可以安全简单地实现一个自定义的图片过滤器。举个栗子,在文章的最后,我们会写如下代码:
let myFilter = blur(blurRadius) >|> colorOverlay(overlayColor)
let result = myFilter(image)
自定义过滤器首先模糊图片然后给模糊后的图片加一层覆盖层。为了实现这个目标,我们需要用到Swift中的第一类函数。点我获取示例。
过滤类型
CI框架中最重要得一个类是CIFilter
,用来创建图片过滤器。当你实例化一个CIFilter
对象,你总是通过kCIInputImageKey
key来提供一个原始图片,然后通过kCIOutputImageKey
key来获取过滤后的图片。然后你又可以使用结果进行下一次的过滤。
这个章节我们会改进下这个API,我们会将KV键值对封装起来并提供一个安全,健壮的API给用户们。我们将定义一个带图片作为参数且返回值为新图片的函数我们自己的过滤器类型:
typealias Filter = CIImage -> CIImage
这个函数类型是我们的基础类型哦~ 很重要!
如果你没有用过函数式编程,因为把一个函数取名叫Filter
看上去略显奇怪。通常,我们命名一个类,让人知道所标记东西是一个函数的考虑优先级比较高。比如我们可以取名叫FilterFunction
或者类似的名字。但是,我们故意取名叫Filter
,因为在函数式编程中有这么一个哲学理念那就是函数仅仅也是一个值。它们与结构体,整数,组值,或者说类时无差别的。这个东西需要一段时间去适应,但是之后你会感觉到它的意义所在。
构建过滤器
现在我们拥有已定义的Filter
类型,我们可以开始定义函数去构建特定的过滤器。这里有一些比较常用的带有必须参数的函数来实现特定的过滤器。这些函数大概都长如下的样子:
func myFilter(/* parameters */) -> Filter
注意到返回值是Filter
,同样是一个函数。后面,这个特性会帮助我们组成多重的过滤器来达到我们想要的图片效果。
为了让我们生活轻松点(好残酷),我们将扩展CIFilter
类带一个便捷构造器并且检索获取到输出图片。
typealias Parameters = Dictionary<String, AnyObject>
extension CIFilter {
convenience init(name: String, parameters: Parameters) {
self.init(name: name)
setDefaults()
for (key, value : AnyObject) in parameters {
setValue(value, forKey: key)
}
}
var outputImage: CIImage { return self.valueForKey(kCIOutputImageKey) as CIImage }
}
便捷构造器带过滤器的名字以及一个字典作为参数。字典中的被赋值KV键值对在新的过滤器对象中将被当做参数。我们的便捷构造器同样需要首先调用设计构造器。
// 略段
模糊
首先我们先定义我们第一个过滤器。高斯模糊过滤只需要带一个模糊指数作为参数。我们可以很轻松的写一个模糊Filter:
func blur(radius: Double) -> Filter {
return { image in
let parameters : Parameters = [kCIInputRadiusKey: radius, kCIInputImageKey: image]
let filter = CIFilter(name:"CIGaussianBlur", parameters:parameters)
return filter.outputImage
}
}
这样就OK了。模糊函数返回一个函数,这个函数带一个image作为参数并且返回一个新的image。因此返回值符合之前定义的CIImage -> CIImage
。
这个栗子只是简单对CI已存在的接口进行了封装。我们可以用相同的手法区创建我们自己的过滤函数。
颜色覆盖
让我们定义一个过滤器来实现对图片进行指定性固定颜色的色层覆盖。CI显然没有这个过滤器,但是我们显然可以从现有的搭一个出来。
未完待续。。。。。。。。。。。