Scala中的Loan Pattern
資源回收是計算機工程實踐中一項重要的實現模式。對於具有GC的程序設計語言,它僅僅實現了內存資源的自動回收,而對於諸如文件IO,數據庫連接,Socket連接等資源需要程序員自行實現資源的回收。
該問題可以形式化地描述爲:給定一個資源R,並將資源傳遞給用戶空間,並回調算法f: R => T;
當過程結束時資源自動釋放。
- Input: Given resource: R
- Output:T
- Algorithm:Call back to user namespace: f: R => T, and make sure resource be closed on done.
因此,該實現模式也常常被稱爲「借貸模式」,是保證資源自動回收的重要機制。本文通過using的抽象控制,透視Scala在這個領域的設計技術,以便鞏固「按名傳遞」技術的應用。
簡單實現
/**
* 名字調用和值調用
*/
object manage {
def apply[R <: {def close() : Unit}, T](resource: => R)(f: R => T) = {
var res: Option[R] = None
try {
res = Some(resource)
f(res.get)
} catch {
case NonFatal(ex) => println(s"Non fatal exception! $ex")
} finally {
if(res != None) {
println(s"Closing resource ...")
res.get.close()
}
}
}
}
等價於
object using {
type Closeable = { def close(): Unit }
def apply[T <: Closeable, R](resource: => T)(f: T => R): R = {
var source = null.asInstanceOf[T]
try {
source = resource
f(source)
} finally {
if (source != null) source.close
}
}
}
統計一個文件的行數調用如下:
object TryCatchARM extends App{
def countLines(fileName:String): Unit = {
println("count file lines")
using(Source.fromFile(fileName)) { source =>
val size = source.getLines.size
println(s"file $fileName has $size lines")
if(size > 20) throw new RuntimeException("Big file")
}
}
args foreach(arg => countLines(arg))
}
R <: {def close() : Unit}
等價於type Closeable = { def close(): Unit }
定義了一個Closeable
的類型別名,使得T必須是具有close方法的子類型,這是Scala支持「鴨子編程」的一種重要技術。例如,File滿足T類型的特徵,它具有close方法。
這樣,不管是什麼對象,只要它擁有一個close():Unit的函數,都可以在這裏使用,並且這個close最後將被自動調用。
惰性求值
resource: => T是按照by-name傳遞,在實參傳遞形參過程中,並未對實參進行立即求值,而將求值推延至resource: =>
T的調用點。
對於本例,using(Source.fromFile(source))
語句中,Source.fromFile(source)
並沒有馬上發生調用並傳遞給形參,而將求值推延至source = resource語句。