勉強日記

チラ裏

PoEAA ch9 Table Module

martinfowler.com


A single instance that handles the business logic for all rows in a database table or view.

Table Module

  • OOのキーの一つ: データと振る舞いとをひとまとめにする
  • 伝統的なOOにおけるオブジェクト
    • アイデンティティをもつ
    • 1オブジェクトは1つの個に対応する
    • メソッドは1つの個に対する操作
    • Domain Modelが立脚するのはこれ
  • Domain Modelの問題点
  • Table Module
    • ふるまい
      • 1クラス1テーブル
    • データ
      • 1オブジェクト1テーブル
      • cf. Domain Modelは1オブジェクト1レコードとか

How It Works

  • つよみ
    • データと振る舞いとのパッケージング
    • RDBとの親和性
  • Table Moduleオブジェクトはアイデンティティをもたない
    • orderテーブルを扱うなら、orderの集合に対応する
    • ので、メソッドを呼び出すときは毎回アイデンティティを指定する
      • 主キーとか
  • Table Moduleを使うときはふつう、テーブル指向的なデータ構造が裏にある
    • SQL問い合わせ結果をRecord Setとして保持したものとか
  • 同一のRecord Setに複数のTable Moduleで操作を行うこともある
    • theDatasetとする
  • 対象は実表とは限らない
    • クエリ結果
    • ビュー
  • Table Moduleの実装をどうする
  • インスタンスがおすすめ
    • theDataで初期化できる
    • 継承できる
  • theDataをどうやってDBから取得する
    • Table Moduleのファクトリメソッドとして生やす
      • 【補】テーブルごとに継承してoverrideする
    • Table Data Gatewayを別途用意する
      • デメリット
        • 別途クラスを用意しなければならないこと
      • メリット
        • Table Moduleは1クラスで済ませらせる
          • かわりにTable Data Gatewayをテーブルごとに用意する
          • DBと接続しないテスト用モックを用意することもできる
            • メモリ上でRecord Setをでっち上げる
  • Table Moduleという名前
    • 1実表1クラス感ある
    • (ビューやクエリに対して作っても)ええんやで

When to Use It

  • Table Moduleはテーブル指向データに基づいている
    • Record Setデータ構造がコードの中心となる
  • Table ModuleはOOの力をフルには発揮できない
  • 複雑なドメインロジックの表現力と、テーブル指向的なデータ構造との親和性とはトレードオフ
    • 前者: Domain Model
    • 後者: Table Module
  • Domain ModelとDBのテーブルとが似通っているなら、
    Active Recordを使いDomain Modelを採用するとよい
  • アプリケーションの他の部分(GUIフレームワークとか)がテーブル指向データに基づいてできているなら、
    Table Module のほうが Domain Model + Active Record よりもうまくいく
    • .NETのADO: ActiveX Data Objectsとか

Example: Revenue Recognition (C#)

  • テーブルからの読み出しは共通なので、基底TableModuleクラスに実装して、テーブル別TableModuleクラスで継承するなど
  • 言語機構を上手に使うと捗る
    • 例: C#のインデクサ
      • 主キー等を添字に指定して、(連想)配列のごとくアクセスできる
class Contract...

    public DataRow this [long key] {
        get {
            String filter = String.format("ID = {0}", key);
            return table.Select(filter)[0];
        }
    }
  • カプセル化の話
    • 何か追加の機能がある場合のみ行う
      • コードの多重化を避けるため
    public enum ProductType {WP, SS, DB};

class Product...

    public ProductType GetProductType (long id) {
        String typeCode = (String) this[id]["type"];
        return (ProductType) Enum.Parse(typeof(ProductType), typeCode);
    }
  • 基本的にはカプセル化しない
    • UIにテーブル指向データを食わせた場合、直接データアクセスされてしまうため
      全部にsetter/getterを生やす、ということに意味がない