Шаблон Loaner в Scala
Scala в глубине демонстрирует шаблон Loaner:
def readFile[T](f: File)(handler: FileInputStream => T): T = {
val resource = new java.io.FileInputStream(f)
try {
handler(resource)
} finally {
resource.close()
}
}
Пример использования:
readFile(new java.io.File("test.txt")) { input =>
println(input.readByte)
}
Этот код выглядит простым и понятным. Что такое "анти-шаблон" шаблона Loaner в Scala, чтобы я знал, как его избежать?
Ответы
Ответ 1
Убедитесь, что все, что вы вычислили, оценивается с нетерпением и больше не зависит от ресурса. Scala делает ленивые вычисления довольно простыми. Например, если вы обернете scala.io.Source.fromFile
таким образом, вы можете попробовать
readFile("test.txt")(_.getLines)
К сожалению, это не работает, потому что getLines
является ленивым (возвращает итератор). И Scala не имеет отличного способа указать, какие методы ленивы, а какие нет. Так что вам просто нужно знать (документы будут вам рассказывать), и вы должны выполнить эту работу до возвращения:
readFile("test.txt")(_.getLines.toVector)
В целом, это очень полезная модель. Просто убедитесь, что все обращения к ресурсу завершены до выхода из блока (поэтому нет незавершенных фьючерсов, без ленивых валов, зависящих от ресурса, без итераторов, без возврата самого ресурса, без потоков, которые не были полностью прочитаны, и т.д.. Конечно, любая из этих вещей в порядке, если они не зависят от открытого ресурса, а только от некоторой полностью вычислимой величины, основанной на ресурсе).
Ответ 2
С шаблоном займа важно знать, когда "бит" кода, который на самом деле вызовет ваш заемный ресурс, будет использовать его.
Если вы хотите вернуть будущее из шаблона кредита, я советую не создавать его внутри функции, которая передается функции шаблона кредита.
Не пишите
readFile("text.file")(future { doSomething })
но do:
future { readFile("text.file")( doSomething ) }
то, что я обычно делаю, заключается в том, что я определяю два типа функций шаблона кредита: Синхронный и Async
Итак, в вашем случае я бы:
def asyncReadFile[T](f: File)(handler: FileInputStream => T): Future[T] = {
future{
readFile(f)(handler)
}
}
Таким образом, вы избегаете вызова закрытых ресурсов. И вы повторно используете уже проверенный и, надеюсь, правильный код синхронной функции.