成功的组件模型都依赖于非常简单的基础,这些基础使得组件能够创建、生成更加复杂的结构。
面向对象编程从未建立过这种基础性、通用的标准,其中组件的基础单位是对象,但对象还不够底层。每个开发者都会为客户创造一个新的“标准”,没有任何团队能为客户应该有哪些字段达成一致,因为它们需要满足客户在不同场景下的不同需求,因而需要不同的数据和运算方式。
在函数式编程语言的集合类型中,像List、VectorMap等,它们都共享一些共同的操作,这些操作大多定义在Seq这个抽象特征中。除了foreach操作外,所有操作都是纯净的高阶函数,没有副作用,且都接受函数作为参数,具体工作由该函数完成。这种高阶函数与离散数据中的组合器(combinator)概念非常接近。
我们可以将这些组合器串联起来,从而用很少的代码完成复杂的功能。对于特定问题,我们可以将数据和需要实现的行为分离。这与通常的面向对象编程的方法正好相反,面向对象总是将数据和行为绑定在一起。在自定义的类中创建所需逻辑的临时实现,是面向对象的典型做法
case class Employee (
name: String,
title: String,
annualSalary: Double,
taxRate: Double,
insurancePremiumsPerWeek: Double)
defined class Employee
val employees = List(
Employee("Buck Trends", "CEO", 200000, 0.25, 100.0),
Employee("Cindy Banks", "CFO", 170000, 0.22, 120.0),
Employee("Joe Coder", "Developer", 130000, 0.20, 120.0)
)
employees: List[Employee] = List( Employee("Buck Trends", "CEO", 200000.0, 0.25, 100.0), Employee("Cindy Banks", "CFO", 170000.0, 0.22, 120.0), Employee( "Joe Coder", "Developer", 130000.0, 0.2, 120.0 ) )
定义Employee类放置雇员的字段类型,实际应用中,这些数据可能从数据库中加载。Employee类型很简单,只需要定义少量行为,在经典的面向对象设计中,我们可能会给Employee添加很多行为,用于工资计算或实现其他域的逻辑。在这里所选择的设计提供了最佳的分离,同时这种设计非常简洁。如果Employee的结构发生变化,需要修改代码时,维护的成本非常小。
// 计算每周工资单
val netPay = employees map { e =>
val net = (1.0-e.taxRate) * (e.annualSalary / 52.0) -
e.insurancePremiumsPerWeek
(e, net)
}
netPay: List[(Employee, Double)] = List( ( Employee( "Buck Trends", "CEO", 200000.0, 0.25, 100.0 ), 2784.6153846153848 ), ( Employee( "Cindy Banks", "CFO", ...
// 打印工资单
println("** Paychecks:")
netPay foreach {
case (e, net) => println(f" ${e.name+':'}%-16s ${net}%10.2f")
}
** Paychecks: Buck Trends: 2784.62 Cindy Banks: 2430.00 Joe Coder: 1880.00
// 生成报表
val report = (netPay foldLeft (0.0, 0.0, 0.0)) {
case ((totalSalary, totalNet, totalInsurance), (e, net)) =>
(totalSalary + e.annualSalary / 52.0,
totalNet + net,
totalInsurance + e.insurancePremiumsPerWeek)
}
report: (Double, Double, Double) = (9615.384615384615, 7094.615384615385, 340.0)
println("\n** Report:")
println(f" Total Salary: ${report._1}%10.2f")
println(f" Total Net: ${report._2}%10.2f")
println(f" Total Insurance: ${report._3}%10.2f")
** Report: Total Salary: 9615.38 Total Net: 7094.62 Total Insurance: 340.00