2014-11-20 72 views
1

,我有以下實施方案,其中我想在任何致命的異常處理合適的資源收盤:斯卡拉嘗試用最後的最佳實踐

private def loadPrivateKey(keyPath: String) = { 
    def tryReadCertificate(file: File): Try[BufferedReader] = Try { new BufferedReader(new FileReader(file)) } 

    def tryLoadPemParser(reader: BufferedReader): Try[PEMParser] = Try { new PEMParser(reader) } 

    def createXXX(buffReader: BufferedReader, pemParser: PEMParser) = try { 
    ... 
    } finally { 
     buffReader.close() 
     pemParser.close() 
    } 
    tryReadCertificate(new File(keyPath, "myKey.pem")) match { 
     case Success(buffReader) => tryLoadPemParser(buffReader) match { 
     case Success(pemParser) => createXXX(buffReader, pemParser) 
     case Failure(fail) => 
     } 
     case Failure(fail) => 
    } 
    } 

我已經看到我的巢式病例塊是一個爛攤子。有一個更好的方法嗎?最後,我只想確保關閉BufferedReaderPEMParser

+1

'Try'使得當它是你想要的類型最有意義無論出於何種原因而不是例外,都會返回到您的API中看起來這個例子並沒有使用'Try'的任何特性,所以最簡單的解決方案就是不使用它。否則,不要在你的內部函數中使用它,而只能在外部表達式中使用它。 – jrudolph 2014-11-20 16:41:25

+0

可能不是你想要的答案,但我認爲最好的做法是使用[scala-arm](https://github.com/jsuereth/scala-arm)。 – lmm 2014-11-20 16:59:50

+0

我不想使用scala-arm – sparkr 2014-11-21 14:49:05

回答

0

你可以調整你的代碼有點像這樣,使用換理解,以清理一些嵌套case語句:

def tryReadCertificate(file: File): Try[BufferedReader] = Try { new BufferedReader(new FileReader(file)) } 

def tryLoadPemParser(reader: BufferedReader): Try[PEMParser] = Try { new PEMParser(reader) } 

def createXXX(buffReader: BufferedReader, pemParser: PEMParser) = { 
    ... 
} 

val certReaderTry = tryReadCertificate(new File(keyPath, "myKey.pem")) 
val pemParserTry = for{ 
    certReader <- certReaderTry 
    pemParser <- tryLoadPemParser(certReader) 
} yield { 
    createXXX(certReader, pemParser) 
    pemParser 
} 

certReaderTry foreach(_.close) 
pemParserTry foreach (_.close) 

結構這樣,你將只落得調用close的事情你確定已成功打開。

更妙的是,如果你的PEMParser發生延長java.io.Closeable,這意味着Try小號都包裹Closeable對象,那麼你可以交換最後兩行的一行是這樣的:

(certReaderTry.toOption ++ pemParserTry.toOption) foreach (_.close) 

編輯

爲響應OP的評論:在第一個例子,如果tryreadCertificate成功,那麼certReaderTry將是一個Success[BufferedReader]因爲它是成功的,叫foreach就會產生BufferedReader,然後它會關閉它。如果certReaderTrySuccess,那麼(通過for-comp),我們將調用tryLoadPemParser,如果這也成功,我們可以轉到createXXX並將tryLoadPemParser分配給pemParserTryval。然後,如果pemParserTrySuccess,那麼foreach產生PEMParser也會發生同樣的情況,我們可以關閉它。根據這個例子,只要那些Trys是成功的,並且其他意想不到的事情(例如在createXXX中)將會一直拋出一個異常,那麼你可以保證在最後關閉相關代碼其工作和關閉這些資源。

EDIT2

如果你在一個單獨的Try想從createXXX值,那麼你可以做這樣的事情:

val certReaderTry = tryReadCertificate(new File(keyPath, "myKey.pem")) 
val pemParserTry = certReaderTry.flatMap(tryLoadPemParser) 
val resultTry = for{ 
    certReader <- certReaderTry 
    pemParser <- pemParserTry 
} yield createXXX(certReader, pemParser) 
+0

close方法如何保證運行? – sparkr 2014-11-21 14:48:46

+0

如果無論誰downvoted提供了一個評論,爲什麼 – cmbaxter 2014-11-21 14:56:58

+0

@ user3102968,我編輯我的答案,以提供更多的細節會很好。 – cmbaxter 2014-11-21 14:57:34