再一次读到 Scala 中的“贷出模式(Loan Pattern)”这个术语,这才比上次稍了解一些。我的理解,此一模式大概是说,对于那些资源密集(resource-intensive)型对象的使用应该使用这一模式。

使用这一模式的原因是,既然资源集中在一个对象中,那么用户代码就不能一直保持着获得的所有资源,而应该在需要时就向资源供给方进行借贷,使用完毕之后立即归还。

此外,Scala 中将函数也是对象,可以像参数那样传递给另一个函数的特征使得贷出模式更加有意义。客户代码借贷了所需的资源,接下来如何使用这些资源以完成特定的任务则由客户决定。就像我们向银行贷款,这些钱的具体用途是客户决定,也是客户才明确的。

对于此类资源,有数据库连接、IO操作等,这些是我们用完则务必立即释放的资源。而且,资源使用完毕业意味着将被自动回收,我们不必操心资源回收的过程。

下面是一个 Scala 脚本代码,客户提供一个文件名,利用writeFile 方法中能够一个向该文件写入内容的 PrintWriter 对象。而具体如何使用这个 PrintWriter 对象则由用户确定,使用完则由自动释放。 

  1. def writeFile(fileName: File)(operationPrintWriter => Unit) { 
  2.     val writer = new PrintWriter(fileName)  // 贷出资源writer 
  3.     try
  4.         operation(pw)   // 客户使用资源 
  5.     }finally { 
  6.         writer.close()  // 用完则释放被回收 
  7.     } 

 

上面 writeFile 方法的定义使用到了 Scala 中的curring技巧,所以才会看到 writeFile 定义之后有两个参数列表(即两个小括号对),后面的参数 operation 用于传入客户具体想要的操作(即怎么使用资源)。 

客户代码如下: 

  1. val file = new File("test.txt"
  2. writeFile(file) { 
  3.     // 使用资源的具体操作 
  4.     writer => writer.println("haolloyin ...\r\n" + 
  5. new java.util.Date()) 

 

执行该脚本,会发现在当前目录下生成一个 test.txt 文件,里面的内容如下:

  1. haolloyin... 
  2. Fri Sep 03 22:44:18 CST 2010 

在这个例子中,客户对资源的使用完全是自定义的,如上面的writer => writer.println(…) 所示。但是有时候我们会遇到一些本身已经预定义好所具备操作的资源,那么此时可以结合 Singleton Object 单例对象的使用特点对其伴生类所代表的资源进行创建和使用。  

同时使用资源类与其单例对象的用意在于,确保该资源只能通过伴生对象进行合法访问,控制好资源的客户源的合法性。这在《Scala 铅笔书》中有下面这个例子,我把代码拷贝并注释如下:

  1. // 主构造器私有,限制其对象(资源)的创建范围 
  2. class Resource private() { 
  3.   println("Starting transaction...")    // 被作为主构造器的语句而执行 
  4.    
  5.   // 资源的收回也是内部处理 
  6.   private def cleanUp() { println("Ending transaction...") } 
  7.    
  8.   // 资源类本身预定义的具体操作 
  9.   def op1 = println("Operation 1")  
  10.   def op2 = println("Operation 2"
  11.   def op3 = println("Operation 3"
  12.  
  13.  
  14. // 单例对象,客户使用资源的入口 
  15. object Resource { 
  16.   def use(codeBlockResource => Unit) { 
  17.     val resource = new Resource  // 贷出资源 
  18.      
  19.     try { 
  20.       codeBlock(resource)    // 使用资源 
  21.     } 
  22.     finally { 
  23.       resource.cleanUp()    // 资源被回收 
  24.     } 
  25.   } 

客户代码如此使用资源:  

  1. Resource.use { resource => 
  2.   resource.op1 
  3.   resource.op2 
  4.   resource.op3 
  5.   resource.op1 // 任意的操作调用序列 

    程序输出:

  1. Starting transaction... 
  2. Operation 1 
  3. Operation 2 
  4. Operation 3 
  5. Operation 1 
  6. Ending transaction...