Шаблон 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)
  }
}

Таким образом, вы избегаете вызова закрытых ресурсов. И вы повторно используете уже проверенный и, надеюсь, правильный код синхронной функции.