2014-11-20 44 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