在上一节看到了scala的在实例一级的选择性混入就不得不感叹scala在语法上的扩展性。就通过这样一个特性scala简化了很多在java中的编程概念和设计模式。

比如说在java中常用的组合,以及装饰模式。下面看个书中的例子,详细说说如何使用trait进行装饰。

假设我们要对一个人进行检查,包括信用记录、收支记录、犯罪记录和工作记录等。但是我们并不会总是都会用到所有的检查,比如要买房时会检查信用记录和收支记录,申请出境时会检查犯罪记录和工作记录。

想想该怎么做:最简单的思路是为每种检查创建一个方法,然后在需要的时候将这些方法组合起来。更常用的方案是创建一个检查接口,为每种检查创建一个类,并实现检查方法。为基础的检查,比如信用记录、收支记录创建检查等创建一个基础类还是可行的,但是如果要为每一项具体的检查创建一个类就会面临一个问题:类膨胀。scala可以让我们避免这种问题,使用trait混入实例可以实现面对不同的检查提供不同的处理方案这样的灵活性。

首先我们需要定义一个抽象类Checker,实现一些在检查中的通用的行为:

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

然后为每一类基础性的检查创建一个trait

trait CreditCheck extends Check {
override def check(): String = "Checked Credit..." + super.check()
} trait BalanceCheck extends Check{
override def check(): String = "Checked Balance..." + super.check()
} trait EmploymentCheck extends Check {
override def check(): String = "Checked Employment..." + super.check()
} trait CriminalRecordCheck extends Check {
override def check(): String = "Check Criminal Records..." + super.check()
}

这些trait都继承自Check。因为我们只想把它混入继承自Check的类。这样做有两个好处:这些trait只能混入继承自Check的类;这些trait可以使用Check的方法。

在每个trait里,都重写了check方法,在重写的同时也引用了Check类的方法,这也可以说是对Check类的修饰或增强。

trait里,通过super调用的方法会经历一个延迟绑定的过程。这个调用并不是对基类的调用,而是对其左边混入的trait的调用——如果这个trait已经是混入的最左trait,那么这个调用就会解析成混入这个trait的类的方法。具体如何可以看一下下面的例子是如何实现的。

来看一个买房申请:

val apartmentApplication = new Check with CreditCheck with BalanceCheck

println(apartmentApplication check)

在创建Check实例的同时混入了两个trait:CreditCheck和BalanceCheck。同样的也可以这样实现出境申请:

val exitApplication = new Check with CriminalRecordCheck with EmploymentCheck

println(exitApplication  check)

可以看出,如果想要按照不同的组合进行检查的话,只需要按照希望的组合将trait混入即可。

上面两段代码的执行结果如下:

最右的trait开始调用check()。然后,顺着super.check(),将调用传递到其左边的trait。最左的trait调用的是真正实例的check()。

在Scala里,trait是一个强有力的工具,可以用它混入横切关注点。使用它们可以以较低的成本创建出高度可扩展的代码。无需创建一个拥有大量类和接口的层次结构,就可以快速地把必要的代码投入使用。

###########

scala学习手记33 - 使用trait进行装饰的更多相关文章

  1. scala学习手记31 - Trait

    不知道大家对java的接口是如何理解的.在我刚接触到接口这个概念的时候,我将接口理解为一系列规则的集合,认为接口是对类的行为的规范.现在想来,将接口理解为是对类的规范多少有些偏颇,更恰当些的观点应该是 ...

  2. Spark基础-scala学习(三、Trait)

    面向对象编程之Trait trait基础知识 将trait作为接口使用 在trait中定义具体方法 在trait中定义具体字段 在trait中定义抽象字段 trait高级知识 为实例对象混入trait ...

  3. scala学习手记34 - trait方法的延迟绑定

    trait的方法的延迟绑定就是先混入的trait的方法会后调用.这一点从上一节的实例中也可以看出来. 下面再来看一个类似的例子: abstract class Writer { def write(m ...

  4. scala学习手记32 - trait选择性混入

    继续上一节. 狗当然是人类的好朋友.但是藏獒呢?这玩意儿又蠢又笨又凶狠,肯定不能算很多人的好朋友了.其实,刚才那句话还可以修正一下下:我们接受的狗才是我们的好朋友. 用程序怎么实现呢?在java里面, ...

  5. scala学习笔记-面向对象编程之Trait

    将trait作为接口使用 1 // Scala中的Triat是一种特殊的概念 2 // 首先我们可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似 3 // 在triat中可 ...

  6. scala学习手记40 - 使用case类

    前面两节我们已经多次接触过case关键字了.case关键字不仅可以用在match/case中来执行模式匹配,也可以用来修饰类.不过用case修饰的类也主要是用来做模式匹配.在上一节曾经提到过match ...

  7. scala学习手记38 - 方法命名约定和for表达式

    方法命名约定 之前在学习<运算符重载>一节时曾经说过一个方法命名约定:方法的第一个字符决定了方法的优先级.现在再说另一个命名约定:如果方法以冒号(:)结尾,则调用目标是运算符后面的实例. ...

  8. scala学习手记23 - 函数值

    scala的一个最主要的特性就是支持函数编程.函数是函数编程中的一等公民:函数可以作为参数传递给其他函数,可以作为其他函数的返回值,甚至可以在其它函数中嵌套.这些高阶函数称为函数值. 举一个简单的例子 ...

  9. scala学习手记17 - 容器和类型推断

    关于scala的类型推断前面已经提到过多次.再来看一下下面这个例子: import java.util._ var list1: List[Int] = new ArrayList[Int] var ...

随机推荐

  1. ZOJ 3332 Strange Country II

    Strange Country II Time Limit: 1 Second      Memory Limit: 32768 KB      Special Judge You want to v ...

  2. Decorate设计模式

    定义:装饰器设计模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案.不改变对象的情况下,动态增加其功能. • 需要扩展一个类的功能,或给一个类添加附加的成员. • 需要动态地给一个对象增 ...

  3. 【Python算法】列表中的 append 比 insert 效率高的实质

    append 与 insert 对比: # append 操作 >>> count = 10**5 >>> nums = [] >>> for i ...

  4. Universally Unique Identifier amazonservices API order 亚马逊订单接口的分析 NextToken

    one hour in linux mysql> ) from listorders; +----------+ | count() | +----------+ | | +---------- ...

  5. 指定文件夹 指定文件后缀名 删除整个文件夹 git 冲突解决 create a new repository on the command line push an existing repository from the command line

    http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/001375840038939c2 ...

  6. FW: git internal

    Git 内部原理 不管你是从前面的章节直接跳到了本章,还是读完了其余各章一直到这,你都将在本章见识 Git 的内部工作原理和实现方式.我个人发现学习这些内容对于理解 Git 的用处和强大是非常重要的, ...

  7. 原!tomcat启动超时(打印了几行日志,后面没了。也不报错,处于启动状态,直到超时)

    项目框架:spring+struts2+mybatis 今天优化代码,改了一堆mybatis dao和xml文件,启动项目时,就出现如标题描述的状况:打印了几行日志,后面就不打印了,也不报错,处于启动 ...

  8. jsp创建cookie

    <jsp:include flush="true" page="header.jsp" /> <script type="text/ ...

  9. Library Cache优化与SQL游标

    Library Cache主要用于存放SQL游标,而SQL游标最大化共享是Library Cache优化的重要途径,可以使SQL运行开销最低.性能最优. 1 SQL语句与父游标及子游标 在PL/SQL ...

  10. 解释一下python中的赋值运算符

    我们将所有的算术运算符和赋值运算符号放在一起展示 a=7 a+=1 print(a) a-=1 print(a) a*=2 print(a) a/=2 print(a) a**=2 print(a) ...