Loanパターンのアレに Conceptパターンを足してみたよ

Loanパターンをfor式で使えるようにしてみたよ に Conceptパターンを足してみました。

と言うのも、後始末のメソッドは close() だけとは限らず、リソースによって dispose() や release() destroy() など様々あります。それら全部に対応できるといいなと思ってやってみました。

trait Closer[-A] {
  def close(value: A)
}

class Loan[A] private (value: A, closer: Closer[A]) {

  def foreach[B](f: A => B): B = try {
    f(value)
  } finally {
    closer.close(value)
  }  

}
object Loan {
  
  def apply[A](value: A)(implicit closer: Closer[A]) = new Loan(value, closer)
  
  type Closeable = {def close()}
  implicit val closeable = new Closer[Closeable] {
    def close(value: Closeable) = value.close()
  }

  type Releasable = {def release()}
  implicit val destroyable = new Closer[Releasable] {
    def close(value: Releasable) = value.release()
  }
  
  type Disposable = {def dispose()}
  implicit val disposable = new Closer[Disposable] {
    def close(value: Disposable) = value.dispose()
  }

}

Closerというのがパターンで言う Concept役ですね。後片付けを担う Concept です。

こうすると何が嬉しいかというと、次のように FileLock なんかも忘れずに release() してくれます。便利。

object Main {
  
  def main(args: Array[String]) {
    import java.io._
    import Loan._
    
    for {
      in      <- Loan(new FileInputStream("source.txt"))
      reader  <- Loan(new InputStreamReader(in, "UTF-8"))
      buf     <- Loan(new BufferedReader(reader))
      out     <- Loan(new FileOutputStream("dest.txt"))
      writer  <- Loan(new OutputStreamWriter(out, "UTF-8"))
      lock    <- Loan(out.getChannel.lock())
    } {
      var line = buf.readLine()
      while (line != null) {
        writer.write(line)
        line = buf.readLine()
      }
    }
    
  }
  
}

Closer の実装クラスを増やせば、自前のクラスや他ライブラリの後片付けにも対応できます。リソースの開放メソッドに何かしら引数が必要とか言った場合にも対応できますね。