在上一节看到了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. Spoken English Practice(I'm gonna do something I never thought I'd be able to)

    绿色:连读:                  红色:略读:               蓝色:浊化:               橙色:弱读     下划线_为浊化 口语蜕变(2017/7/6) 英 ...

  2. jq封装选项卡写法

    jq普通选项卡写法: var tabTag=$('#tabon'); var tabon=tabTag.find('li');//菜单栏 var tabCon=$(".hidden" ...

  3. elastic search安装与本地测试

    elastic search安装与本地测试 elastic search是一个全文搜索引擎 教程: 综合:http://www.ruanyifeng.com/blog/2017/08/elastics ...

  4. WTForms In Flask(WTForms在Flask中的应用)

    WTForms WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证. 安装wtforms : pip3/pip install wtforms 用户登录/注册示例 项 ...

  5. Oracle11g的安装卸载及经常使用工具的使用

    Oracle11g的安装卸载及经常使用工具的使用 目的: (1) 掌握Oracle 11g数据库的安装与卸载过程. Oracle11g的安装卸载及经常使用工具的使用 目的: (1) 掌握Oracle  ...

  6. 聚合的安全类导航、专业的安全知识学习平台——By Me:)

    以“基于对抗的安全研发”为初衷,让大家在工作中始终有安全意识.安全思维和安全习惯,几年前自己搭建了面向公司内部全员的安全晨报.现在站在“用户“的角度回头看看,觉得科目设计等很多方面都还有很多的不足: ...

  7. Android studio 如何快速收起代码?

    windows下 ctrl+shift+(小键盘上的减号 -) mac下 commang+shift+减号,搞定

  8. node.js---sails项目开发(1)

    1.安装Node.js和npm---这里就做介绍啦! 2.需要全局下安装Sails sudo npm install sails -g 3. 在本地创建一个文件夹 mkdir ~/lsg/sails ...

  9. 用Android NDK编译FFmpeg

    附(2018-01-06):     有一个将x264及lame等库集成进去了且基于android的ffmpeg的编译方法,地址参见:       https://github.com/writing ...

  10. JS的深拷贝

    var obj = { name: "wuyongyu", age: 18 } 第一种方式: function deepClone(obj){ // 判断传入的数据类型 - 数组或 ...