6.5 重用函数值

我们见识到了函数值对创建重用性代码以及消除代码重复有着很大帮助。但是,将方法当作另一个方法的实参嵌进去的做法不利于代码重用。不过,我们可以创建函数值的引用,这样就可以重用它们了,看个例子。

假定有个类Equipment,期待传入一个用于模拟的计算程序。我们可以把计算作为函数值传入构造函数,像这样:

FunctionValuesAndClosures/Equipment.scala

  1. class Equipment(val routine : Int => Int) {
  2. def simulate(input: Int) = {
  3. print("Running simulation...")
  4. routine(input)
  5. }
  6. }

创建Equipment实例时,可以把函数值作为参数传入构造函数。

FunctionValuesAndClosures/EquipmentUseNotDry.scala

  1. val equipment1 = new Equipment({input => println("calc with " + input); input })
  2. val equipment2 = new Equipment({input => println("calc with " + input); input })
  3. equipment1.simulate(4)
  4. equipment2.simulate(6)

输出如下:

  1. Running simulation...calc with 4
  2. Running simulation...calc with 6

上面的代码里,我们想对两个Equipment实例使用同样的计算代码。然而,这样创建的代码是重复的、不够DRY,如果修改计算,就不得不改动两处。如果只需创建一次且可以重用会好一些。方法是把函数值赋给val,然后重用它,像这样:

FunctionValuesAndClosures/EquipmentUseDry.scala

  1. val calculator = { input : Int => println("calc with " + input); input }
  2. val equipment1 = new Equipment(calculator)
  3. val equipment2 = new Equipment(calculator)
  4. equipment1.simulate(4)
  5. equipment2.simulate(6)

上面代码的输出如下:

  1. Running simulation...calc with 4
  2. Running simulation...calc with 6

这里把函数值存在一个名为calculator的引用里。定义这个函数值时,Scala需要一些类型信息方面的小帮助。在稍早的例子里,Scala可以根据调用的上下文推演出inputInt。不过,既然把函数值独立定义出来,就需要告诉Scala参数的类型。随后,把引用的名字作为实参传给已创建的两个实例的构造函数。

上面的例子里,为函数值创建了一个引用calculator。如果你习惯于在函数或方法内定义引用或变量,也许这种做法会让你觉得更自然。不过,Scala允许把一个完整的函数放到其他函数里面。这样的话,有一种更地道的方式来达成上面重用的目标。Scala让你很容易把事情做对。在需要函数值的地方,允许传入普通函数。

FunctionValuesAndClosures/EquipmentUseDry2.scala

  1. def calculator(input: Int) = { println("calc with " + input); input }
  2. val equipment1 = new Equipment(calculator)
  3. val equipment2 = new Equipment(calculator)
  4. equipment1.simulate(4)
  5. equipment2.simulate(6)

我们把计算程序创建成一个函数,创建这两个实例时,把函数名作为实参传给了构造函数。在Equipment里,Scala很自然的把它当作了函数值的引用。

用Scala编程,就不必在优秀的设计原则和代码质量上面做妥协。相反,它提升了这些好的实践,用Scala写代码时,就该努力的去运用这些实践。