在Swift 中实现流畅调用
Fluent API是一种面向对象的API风格,它通过方法链来提供一种流畅、连贯的编程体验。Fluent API通常用于构建复杂对象,简化复杂调用过程,提高代码的可读性,同时可以充分利用IDE的代码提示功能,大幅度提高编码效率。
FLuent API 实现的关键是,对象的方法调用返回对象本身,因此,可以在新返回的对象上继续实施调用,由此一气呵成,实现流畅调用。
本文介绍在 swift 语言中如何实现Fluent API。
首先定义一个示例结构。我们设想一个Employee对象,如下:
struct Employee {
var name :String
var address:String
var age:Int
var salary:Float
}
Employee结构有一系列属性,为简单起见,我们假设所有属性都是必须的,该结构体还有一系列的setter用来设置或者修改其属性。在没有实现Fluent调用的情况下,这些setter长得像下面一样:
mutating func setName(_ name :String) {
self.name = name
}
mutating func setAddress(_ address :String) {
self.address = address
}
mutating func setAge(_ age:Int ) {
self.age = age
}
mutating func setSalary (_ salary :Float ) {
self.salary = salary
}
Swift 语言的结构是值对象,如果一个函数要改变其属性值,函数必须用mutating 关键字说明,很碍眼。
调用方式:
var emp = Employee(name:"张三",address:"盘锦大道135号",age:31,salary:15000)
print(emp)
//Employee(name: "张三", address: "盘锦大道135号", age: 31, salary: 15000.0)
emp.setAge(32)
emp.setSalary(18000)
print (emp)
//Employee(name: "张三", address: "盘锦大道135号", age: 32, salary: 18000.0)
下面我们将上述各个setter实现为流畅方法,实现的核心是每个setter返回对象(结构)本身,将上述Setter的返回值由Void修改为结构体本身即可,如下代码所示:
mutating func setName(_ name :String) ->Employee {
self.name = name
return self
}
mutating func setAddress(_ address:String)->Employee {
self.address = address
return self
}
mutating func setAge(_ age:Int )->Employee {
self.age = age
return self
}
mutating func setSalary(_ salary:Float )-> Employee {
self.salary = salary
return self
}
按下述方式调用:
var emp = ...
emp.setAge(32).setSalary(18000)
很遗憾,上述链式调用并不能正确工作,出现如下编译错误:
问题在于,Employee是值对象,当我们将值对象赋值给另外一个变量是,新变量是一个不变量,不允许修改。具体到上述代码中,setSalary() 使用setAge()返回的对象,该操作缺省返回一个 不可变值对象,因此setSalary()不能够对其修改。
解决方法是,显示说明各个setter 的返回对象为var对象,在新的var对象上进行修改,然后返回该对象。
修改后的代码如下:
func setName(_ name :String) ->Employee {
var newself = self
newself.name = name
return newself
}
func setAddress(_ address:String)->Employee {
var newself = self
newself.address = address
return newself
}
func setAge(_ age:Int )->Employee {
var newself = self
newself.age = age
return newself
}
func setSalary(_ salary :Float)->Employee {
var newself = self
newself.salary = salary
return newself
}
修改后,链式调用生效,大功告成!示例如下:
var emp = Employee(name:"张三",address:"盘锦大道135号",age:31,salary:15000)
print(emp)
// Employee(name: "张三", address: "盘锦大道135号", age: 31, salary: 15000.0)
var emp1 = emp.setAge(32).setSalary(18000)
print (emp1)
//Employee(name: "张三", address: "盘锦大道135号", age: 32, salary: 18000.0)
读者应该注意到,修改后的代码没有 mutating了,因为setter函数并没有修改对象自身的属性,而是修改一个新对象的属性,这个新对象是原对象的一个copy,所以不需要mutating了
这也算是上述方式的一个小小的红利吧。
方案改进:使用闭包去掉样本代码
虽然上述方法可以正确工作,当各个setter中存在许多完全相同的样本代码,不够优雅,存在继续优化的空间。下面我们使用闭包去掉这些样本代码。首先为Employee结构增加一个通用的修改函数 modifyprops,如下:
//...
private func modifyprops (_ modifier:(inout Employee) ->Void )->Employee
{
var newself = self
modifier(&newself)
return newself
}
modifyprops接受一个闭包作为参数,该闭包的类型为(inout Employee) -> Void,它接受一个 Employee结构体并对其进行修改 。
上述闭包抽象了修改过程,它集中了不变的样本代码,并将变化的代码通过闭包传递。 以 modifyprops 函数为基础,修改各setter,最终版本如下:
func setName(_ name:String) ->Employee {
return modifyprops {
$0.name = name
}
}
func setAddress(_ address:String)->Employee {
return modifyprops {
$0.address = address
}
}
func setAge(_ age:Int )->Employee {
return modifyprops {
$0.age = age
}
}
func setSalary(_ salary:Float )->Employee {
return modifyprops {
$0.salary = salary
}
}
与前一版本相比,改进后的代码没有了碍眼的样本代码,用户可以集中精力编写setter本身的逻辑。
小结
本文简要介绍了在swift语言中实现Fluent调用方法及注意事项。