第7章——特质

Java 只允许单继承,这会强制建立一种线性的层次结构模型。但现实世界中充满了横切 关注点(crosscutting concerns)—一种横切且影响多个抽象的概念,这些抽象并不同属于 某个单一的类层次结构 ① 。在典型的企业级应用程序中,安全、日志记录、验证、事务以及 资源管理都是这些横切关注点的应用场景。但是,因为我们受限于单一的类层次结构,所以 实现这些横切关注点变得相当困难,往往需要代码上的重复或者引入重量级工具 ② 。Scala 使 用特质(trait)解决了这个问题。

  1. 即横跨应用程序中多个模块的特性。——译者注

7.1 理解特质

UsingTraits/HumanWithListen.scala

class Human(val name: String) {
  def listen(): Unit = println(s"Your friend $name is listening")
}

class Man(override val name: String) extends Human(name)
class Woman(override val name: String) extends Human(name)

UsingTraits/Friend.scala

trait Friend {
  val name: String
  def listen(): Unit = println(s"Your friend $name is listening")
}

UsingTraits/Human.scala

class Human(val name: String) extends Friend

class Woman(override val name: String) extends Human(name)
class Man(override val name: String) extends Human(name)

UsingTraits/Dog.scala

class Dog(val name: String) extends Animal with Friend {
  //optionally override method here.
  override def listen(): Unit = println(s"$name's listening quietly")
}

UsingTraits/Animal.scala

class Animal

UsingTraits/UseFriend.scala

object UseFriend extends App {
  val john = new Man("John")
  val sara = new Woman("Sara")
  val comet = new Dog("Comet")

  john.listen()
  sara.listen()
  comet.listen()

  val mansBestFriend: Friend = comet
  mansBestFriend.listen()

  def helpAsFriend(friend: Friend): Unit = friend.listen()

  helpAsFriend(sara)
  helpAsFriend(comet)
}

运行结果

Your friend John is listening
Your friend Sara is listening
Comet's listening quietly
Comet's listening quietly
Your friend Sara is listening
Comet's listening quietly

7.2 选择性混入

UsingTraits/Cat.scala

class Cat(val name: String) extends Animal

UsingTraits/UseCat.scala

object UseCat extends App {
  def useFriend(friend: Friend): Unit = friend.listen()

  val alf = new Cat("Alf")
  val friend: Friend = alf // ERROR

  useFriend(alf) // ERROR
}

编译结果

UseCat.scala:5: error: type mismatch;
 found   : Cat
 required: Friend
  val friend : Friend = alf // ERROR
                        ^
UseCat.scala:7: error: type mismatch;
 found   : Cat
 required: Friend
  useFriend(alf) // ERROR
            ^
two errors found

UsingTraits/TreatCatAsFriend.scala

def useFriend(friend: Friend): Unit = friend.listen()

val angel = new Cat("Angel") with Friend
val friend: Friend = angel
angel.listen()

useFriend(angel)

运行结果

Your friend Angel is listening
Your friend Angel is listening

7.3 使用特质实现装饰器模式

UsingTraits/Decorator.scala

abstract class Check {
  def check: String = "Checked Application Details..."
}

UsingTraits/Decorator.scala

trait CreditCheck extends Check {
  override def check: String = s"Checked Credit... ${super.check}"
}

trait EmploymentCheck extends Check {
  override def check: String = s"Checked Employment...${super.check}"
}

trait CriminalRecordCheck extends Check {
  override def check: String = s"Check Criminal Records...${super.check}"
}

UsingTraits/Decorator.scala

val apartmentApplication =
  new Check with CreditCheck with CriminalRecordCheck

println(apartmentApplication.check)

UsingTraits/Decorator.scala

val employmentApplication =
  new Check with CriminalRecordCheck with EmploymentCheck

println(employmentApplication.check)

运行结果

Check Criminal Records...Checked Credit... Checked Application Details...
Checked Employment...Check Criminal Records...Checked Application 
Details...

7.4 特质中的方法延迟绑定

UsingTraits/MethodBinding.scala

abstract class Writer {
  def writeMessage(message: String): Unit
}

UsingTraits/MethodBinding.scala

trait UpperCaseWriter extends Writer {
  abstract override def writeMessage(message: String): Unit =
    super.writeMessage(message.toUpperCase)
}

trait ProfanityFilteredWriter extends Writer {
  abstract override def writeMessage(message: String): Unit =
    super.writeMessage(message.replace("stupid", "s-----"))
}

UsingTraits/MethodBinding.scala

class StringWriterDelegate extends Writer {
  val writer = new java.io.StringWriter

  def writeMessage(message: String): Unit = writer.write(message)
  override def toString: String = writer.toString
}

UsingTraits/MethodBinding.scala

val myWriterProfanityFirst =
  new StringWriterDelegate with UpperCaseWriter with ProfanityFilteredWriter

val myWriterProfanityLast =
  new StringWriterDelegate with ProfanityFilteredWriter with UpperCaseWriter

myWriterProfanityFirst.writeMessage("There is no sin except stupidity")
myWriterProfanityLast.writeMessage("There is no sin except stupidity")

println(myWriterProfanityFirst)
println(myWriterProfanityLast)

运行结果

THERE IS NO SIN EXCEPT S-----ITY
THERE IS NO SIN EXCEPT STUPIDITY