2014年8月18日月曜日

Programming in Scala, First Edition: Chapter 27

27. Modular Programming Using Objects
  scalaの特徴の一つはプログラム小さくても大きくても同じ技が使えると言う事
  本章ではどうやって小さい部品から大きいプログラムを作成するかを解説する。
  packageとaccess modifierも有用だが抽象化出来ないという制約がある。
    同一のプログラマ内でpackageは再定義出来ない。
    package間で継承が使えない。
    コードを変更しないでpackageの内容を変更出来ない。
  scalaのOOP機能を使えば更にmodularに出来る。
    singleton objectをmoduleとして使う。
    traitやclassをmoduleとして使う。
    traitを使ってmoduleを複数のファイルに分割する。

27.1 The problem
  modularである事の利点
    別々にcompile可能になる事で複数チームで独立して作業できる。
    実装のある部分を取り外して別のものに置き換える事が出来る。
      開発の段階に合わせてテスト用のstub等から本番の環境に移行できる。
  modularであるために必要な事
    インタフェースと実装の分離。
    インタフェースや再コンパイル無にmoduleを交換可能な事。
    複数のmoduleを組み合わせる方法が用意されている事。
  dependency injectionを使えば上記は可能
    SpringやGuice等のframeworkで実現されている。
    Spring
      インタフェースをJavaのinterface、実装をJavaのclassに対応付けている。
      XMLの設定ファイルを使ってmoduleの依存関係の定義と結合を行う。
    scalaでもSpringを利用する事は出来る。
  scalaの言語機能その物を使った方法を本章で紹介する。
    objectをmoduleとして使う事でmodularityを外部framework無しで実現する。

27.2 A recipe application
  例として料理のレシピを管理するweb appを考える。
  domain layerとapplication layerで分離したい。
    domain layer
      ビジネスロジックの実装や外部のRDBへの保存。
    application layer
      UI layerやclientソフトに対するAPI提供。
  テスト様に各部品をmockと交換可能にした。
    交換可能部分をmodule化する。
  scalaなら大小に関わらず同じようにobjectを構成する事が出来る。
    scalaがscalable languageである所以。
  scalaではsingleton objectをmoduleとして使える。
    objectの中にinner classも定義可能。

27.3 Abstraction
  moduleとして扱うsingleton objectの一部をmockと置き換えて使いたい。
  abstract member, methodを使ったabstract classにすればよい。
    abstract class Database { ... }
    abstract class Browser { val database: Database ... }
  abstract classを継承することで一部を置き換えることが可能。
    object SimpleDatabase extends Database { ... }
    object SimpleBrowser extends Browser { val database = SimpleDatabase }
    object StudentDatabase extends Database {
    object StudentBrowser extends Browser { val database = StudentDatabase }

27.4 Splitting modules into traits
  singleton objectのmoduleが一つのファイルに入れるには大きすぎる場合。
    traitに分離してそれをextends, withで合成すれば良い。
  必ず合成されると分かっているtraitのmemberはself typeを使えば参照できる。
    trait SimpleRecipes {
      this: SimpleFoods =>
      object FruitSalad extends Recipe(
        "fruit salad",
        List(Apple, Pear),   // Now Pear is in scope
        "Mix it all together."
      )
      def allRecipes = List(FruitSalad)
    }
    SimpleFoodsのPearを参照出来る。
      this.Pearと解釈されて、thisがSimpleFoodsなので参照出来る。

27.5 Runtime linking
  mainメソッド内でsingleton objectをmoduleとして定義出来る。
    mainへの引数で別のobjectを生成すれば動的にmoduleの組み合わせを変えられる。
    新しいmoduleが加える場合でもmainのみを変更すれば良い。
  mainがconfiguration fileとして使える。
    SpringのXMLを使った定義よりもcompilerのチェックが使えるので強力。

27.6 Tracking module instances
  path-dependent typeを使う事でInner typeを別々のtypeとして扱う事が出来る。
    あるclassの組から複数のmoduleの組を生成する時に使える。
      moduleを特定の組み合わせに制限出来る。
  実質同じpath-dependent typeになっていてもif式等でコンパイラが追えない場合。
    singleton typeを指定する事で対応出来る。
  abstract class Database {
    case class Category(s: String, n: Int)
    val category: Category
  }
  abstract class Browser {
    val database: Database
    def print(category: database.Category) { println(category.s) }
  }
  object db1 extends Database { val category = Category("a", 1) }
  object db2 extends Database { val category = Category("b", 2) }
  def f(x: Int) {
    val db: Database = if (x == 1) db1 else db2
    object browser extends Browser { val database: db.type = db }
    browser.print(db.category)
  }
    dbとbrowser.databaseは同一だがif式があるのでコンパイラは追えない。
    database: db.typeでコンパイラにdatabaseがdbと同じtypeだと伝えられる。
      db.typeはobject dbに固有のsingleton type

27.7 Conclusion
  objectを使ってmoduleを定義する事でclassによる抽象化が可能になる。
  objectの生成は実行時に行われるので実行時に再コンパイル無でmodule構成可能。
    更に実行後にobjectの内容を書き換えればmoduleを再構成可能。
  モジュール化は大規模プログラミングの一部で試すのが難しい。
    どの様な違いが出るのか試すためには大規模プログラムが必要になるので。
  実際に大規模プログラムを読み書きする時に本章のテクニックを思い出せば良い。

0 件のコメント:

コメントを投稿