Execute Around Method(用完对象后立刻释放)+偏应用函数+闭包

1. Execute Around Method(用完对象后立刻释放)

在Java中,进入synchronized块时,会获得给定对象的监视器(锁)。离开此块时,这个监视器会自动释放。即便是块里的代码抛出未处理的异常,也不会影响到释放。这种确定性行为非常好。
相比于Java,Scala里实现这些构造相当容易。看下面例子:
假如有个类Resource,需要自动开启食物,在用完对象之后,就要显式地结束事物。正确的启动事物可以依赖构造函数,而实现终结部分却有些棘手。这就落入Execute Around Method模式的范畴。我们想在对对象进行任意一组操作的前后执行一对操作。
在Scala中,可以用函数值实现这个模式。下面是Resource的代码,还有它的伴生对象:

class Resource private() {
    println("Starting transaction...")

    private def cleanUp() {
        println("Ending transaction...")
    }

    def op1 = println("Operation 1")

    def op2 = println("Operation 2")

    def op3 = println("Operation 3")
}

object Resource {

    def use(codeBlock: Resource => Unit): Unit = {
        val resource = new Resource

        try {
            codeBlock(resource)
        }
        finally {
            resource.cleanUp()
        }
    }

    def main(args: Array[String]): Unit = {
        Resource.use{resource=>
            resource.op1
            resource.op2
            resource.op3
        }
    }
}

Resource类的构造函数标记为private。这样,就不会在这个类或者它的伴生对象之外创建出类的实例。这样一来,就只能以确定的方式使用对象了,从而保证了其行为是按照确定的方式自行执行。cleanUp()也声明为private。打印语句是真事务操作的占位符。调用构造函数时,事物启动;隐式调用cleanUp()时,事务终结。Resource类可用的实例方法诸如op1()、op2()等。

伴生对象里有一个方法叫作use(),它接收一个函数值作为参数。use()方法创建了一个Resource的实例,在try和finally块里,调用了Resource私有实例方法cleanUp()。这就是对某些必要操作提供确定调用的全部动作。
运行结果如下:

Starting transaction...
Operation 1
Operation 2
Operation 3
Ending transaction...

上面模式的一个变体时Loan模式。如果非内部资源得到确定性释放,就可以使用这个模式。可以这样认为这种资源密集型的对象时借给你的,用过之后应该立即归还。

下面时个使用这个模式的例子:

    def writeToFile(fileName: String)(codeBlock: PrintWriter => Unit) {
        val writer = new PrintWriter(new File(fileName))
        try {
            codeBlock(writer)
        } finally {
            writer.close()
        }
    }

    def main(args: Array[String]): Unit = {
        writeToFile("output.txt") { writer =>
            writer write "hello from scala"  // 输出 hello from scala
        }
    }

作为writeToFile()方法使用者,我们不必操心文件的关闭。在代码块了,这个文件是借给我们用的。我们可以用得到的PrintWriter实例进行写操作,一旦从这个块返回,方法就会自动关闭文件。

2. 偏应用函数

使用函数可以说成是函数应用于实参。如果传入所有的预期的参数,就完全应用了这个函数。如果只传入几个参数,就会得到一个偏应用函数。这有一个便利,可以绑定几个实参,其他的留在后面填写。来看下下面这个例子:

    def log(date: Date, message: String): Unit = {
        println(date + "----" + message)
    }

    def main(args: Array[String]): Unit = {
        val date = new Date
        log(date, "message1")
        log(date, "message2")
        log(date, "message3")
    }

上面的代码里,log()方法有两个参数:date和message。我们想多次调用这个方法,用相同的date、不同的message。通过把log()方法偏应用到date实参上,可以消除每次调用都要传递它的烦恼。
下面代码示例里,先将一个值绑定到date参数上,然后_使第二个参数未绑定,其结果就是一个偏应用函数,将它存到aa这个引用里。现在,就可以只用未绑定的实参message调用这个新的方法了:

        val aa = log(new Date, _: String)
        aa("message4")
        aa("message5")
        aa("message6")

当创建偏应用函数时,Scala内部会创建一个新类,它有一个特殊的apply()方法。调用偏应用函数,实际上是调用这个apply()方法。调用偏应用函数,实际上是调用这个apply方法。

3. 闭包

通常用于函数值或代码块的变量和值都是绑定的。可以清除的知道他们绑定到哪儿,局部变量或是参数。此外,还可以创建有未绑定变量的代码块。调用函数之前,必须绑定它们;不过,它们可以在局部范围和参数列表之外绑定变量。这就是称它们为闭包的原因。
看下面的例子:

    def loopThrough(number: Int)(closure: Int => Unit): Unit = {
        for (i <- 1 to number) {
            closure(i)
        }
    }

loopThrough()方法接收一个代码块作为第二个参数,从1到第一个参数的范围内每个元素都会调用给定的代码块。下面定义一个代码块传给这个方法:

        var result = 0
        val addIt = { value: Int => result += value }

上面的例子里,定义了一个代码块,并将其赋值给变量addIt。在代码块内,变量value绑定到参数。不过,变量result在块或参数列表内是未定义的。实际上,这是绑定到代码块外部的变量result上。代码块伸长了它的手,绑定到一个外部的变量。下面是在调用loopThrough()方法时如何使用代码块:

        loopThrough(10) {addIt}
        println(result) // 55

        loopThrough(1) {addIt}
        println(result) // 56
        
        result = 0
        loopThrough(5) {addIt}
        println(result) // 15

将闭包传给方法loopThrough(),value绑定到传给loopThrough()的参数上,而result则绑定到loopThrough()调用放上下文里的变量上。

绑定并不是获得变量当前值的一份副本;它实际上时绑定到变量本身。因此,如果将result的值重置为0,闭包也会看到这种变化。而且,如果闭包设置了result的值,那么主代码里也可以看到。
下面是另一个例子,闭包绑定到另一个变量product上:

        var product = 1
        loopThrough(5){product *= _}
        println(product) // 120

在这种情况下,_指向loopThrough()所传入的参数,product绑定到loopThrough()的调用方里叫这个名字的变量上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值