我們訪問資源需要關註對資源的鎖定、對資源的申請和釋放,還有考慮可能遇到的各種異常。這些事項本身與代碼的邏輯操作無關,但我們不能遺漏。也就是說進入方法時獲取資源,退出方法時釋放資源。這種處理就進入了Execute Around模式的範疇。 在scala里可以用函數值實現這種模式。下麵是一個示例,使用R... ...
我們訪問資源需要關註對資源的鎖定、對資源的申請和釋放,還有考慮可能遇到的各種異常。這些事項本身與代碼的邏輯操作無關,但我們不能遺漏。也就是說進入方法時獲取資源,退出方法時釋放資源。這種處理就進入了Execute Around模式的範疇。
在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) { val resource = new Resource try { codeBlock(resource) } finally { resource.cleanUp() } } }
這段代碼里將Resource類的構造器標記為private,這樣就只能在Resource類內部和它的伴生類中創建實例了。因為只能在這兩個地方創建實例,從而保證是可以按照確定的方式使用這個類的對象了,也就可以保證其行為是按照確定的方式執行。cleanUp()方法也被標記為private,確保不會被意外調用。第一行的print語句是具體事務操作的占位符。調用構造函數時,事務啟動;調用cleanUp()函數時,事務終結。此外Resource類中還準備了一些實例方法,如op1()、op2()等。
在伴生對象里有一個預設public的方法use,它接收一個函數值作為參數。use()方法創建了一個Resource的實例,在try和finally塊的保護之下,把這個實例傳給了給定的函數值。在finally塊里,調用了Resource私有實例方法cleanUp()。
看一下是如何使用Resource類的:
Resource.use { resource =>
resource.op1
resource.op2
resource.op3
resource.op1
}
代碼輸出結果是:
調用Resource的伴生對象時,會自動創建一個Resource實例,等到傳遞的函數值執行結束後,會自動調用cleanUp方法釋放占用的資源。
上面模式的一個變體是Loan模式。如果想確保非記憶體資源得到確定性釋放,就可以使用這個模式。可以這樣認為這種資源密集型的對象是借給你的,用過之後應該立即歸還。
下麵是一個Loan模式的例子:
import java.io._ def writeToFile(fileName: String)(codeBlock: PrintWriter => Unit) = { val writer = new PrintWriter(new File(fileName)) try { codeBlock(writer) } finally { writer.close() } }
現在調用writeToFile()將一些內容寫入文件:
writeToFile("output.txt") { writer => writer write "hello from Scala" }
方法的執行結果:
作為writeToFile()方法的使用者,我們不必操心文件的關閉。在代碼塊里,這個文件是借給我們用的。我們可以用得到的PrintWriter實例進行寫操作,一旦從這個塊返回,方法就會自動關閉文件。
###############