勉強日記

チラ裏

PoEAA ch18 Registry

martinfowler.com


Registry

A well-known object that other objects can use to find common objects and services.

  • OOPでオブジェクトを検索するとき、通常は関連(association)しているオブジェクトを起点に行う
  • ある顧客customerの注文をすべて取得したい
    • customerと関連していれば、customer.getOrdersAll()で手に入る
  • customerオブジェクトが無い場合は?
    • custmerオブジェクトは、customerFinder.findById(customerId)で手に入る
  • customerFinderオブジェクトが無い場合は?
  • Registryの出番
    • グローバルオブジェクトのようにふるまう
      • 実際にそうかは別問題(後述)

How It Works

  • インタフェースと実装とは異なるよ、という話
    • インタフェースはstaticメソッド
      • グローバルアクセス可能
    • メソッドがstaticだからといって、データまでもがstaticである必要性はない
      • むしろ定数以外でstaticなデータは持たない
  • データのスコープ
    • プロセス内グローバル
    • スレッド内グローバル
    • セッション内グローバル
  • スコープが異なれば、実装も異なってくる
  • が、staticメソッドを叩く人は、得られるデータのスコープを知っている必要はない
  • シングルスレッドならSingleton Pattern使え
    • プロセススコープ
      • データをmutableなstaticフィールドで持っても実現できる
      • が、置換不可能だからよしたほうがいい
        • テストしたいとき困る
    • private static Registry soleInstance;を1つ持つ感じ
    • Registry::soleInstance->getFoo()
    • Registry::getFoo()と書けるとなおよい
  • マルチスレッドならSingletonはやめておけ
    • スレッド横断的にmutableなグローバル領域にアクセスするのは無謀
      • 同期制御すれば不可能ではないが難しい
    • immutableだったり、めったに変更されないデータならこの限りではない
      • 合衆国の州のリストとか
  • 一般的なRegistryはスレッドスコープ
    • 例: DBコネクション
    • スレッドスコープの実装例
      • 環境がスレッド別ストレージを提供している
        • JavaThreadLocal(後述)
      • スレッドをキーとする辞書で自前管理する
  • スコープによらず、Registryのインタフェースは変わらないことに注意
    • Registry::getDbConnection()
    • プロセススコープだろうがスレッドスコープだろうが変わらない
  • セッションスコープでも同じ
    • セッションIDをキーとする辞書で管理
    • …をスレッドスコープの辞書に入れることも
  • スレッドスコープのデータにstaticメソッドでアクセスするとパフォーマンス問題あるかも
  • その場合はスレッド別インスタンスに直接アクセスしてボトルネックを解消せよ
  • Registryが複数あるケース
    • システムのレイヤーや実行コンテキストで区切られるのが普通
    • 著者としては、どう使われるかで分けるのが好み

When to Use It

  • 濫用するな
    • 基本はあくまでオブジェクト間相互作用
  • Registryを使わない方法
    • 広く使われるオブジェクトは明示的にメソッドの引数で渡す
      • デメリット: 自分が使わなくても下の層で使うから受け取らないといけない、というオブジェクトが現れる
      • 渡ってきたパラメータが9割がた使われないならRegistryを検討せよ
    • コンストラクタの引数で渡し、参照を保持する
      • 使わなくても、コンストラクタの引数しか汚れない
  • Registryの問題
    • 管理するデータが追加されるたびに、Registryクラスを変更しないといけない
      • これを嫌って、グローバルデータの保持には汎用の辞書クラスを使う人もいる
  • 著者見解としては、汎用辞書ではなく、明示的なRegistryクラスを作るほうが良い
    • 識別子のあるメソッドを生やせる
      • 何が使えるかソースコードを見ればわかる
      • ドキュメント自動生成もできる
      • 辞書を使うとありがちな「何のキーで何が得られるんだっけ」という混乱がない
      • 型安全
    • 静的なインタフェースで実装をカプセル化できる
      • スコープ
  • グローバルはグローバル。無実を証明しない限りは罪であることを忘れぬよう

Example: A Singleton Registry (Java)

  • どうってことのないSingleton Pattern。略
  • privateなインスタンスフィールドにstubインスタンスをセットするためのメソッドを生やしておく(テスト用)

Example: Thread-Safe Registry

class ThreadLocalRegistry ...

    private static ThreadLocal instances = new ThreadLocal();
     
    public static ThreadLocalRegistry getInstance() {
        return (ThreadLocalRegistry) instances.get();
    }
  • こんな感じにスレッドローカル変数を作れる
    • instances自体はプロセススコープ。辞書みたいなヤツ
    • instances.get()すると、処理中のスレッドをキーに、スレッド固有のインスタンスを取り出してくれる
    • instances.set(何か)すると、処理中のスレッドをキーに、スレッド固有のインスタンスに何かをセットしてくれる

英語

  • asylum
    • 亡命