GoF本 Flyweight

GoFデザインパターン勉強メモ

ねらい

  • 小さなオブジェクトを大量に利用する際、オブジェクトを共用することで空間を節約する

モチベーション

  • クラスの継承ではなく、オブジェクトの集約によるデザインパターンを推してきた
  • 集約のパターンを何も考えずに実装すると、高コストになることがある
  • 例) テキストエディタ

    • 一文字一文字にオブジェクトを割り当てると、10万単位のオブジェクトが生成されてしまう
  • 上記の例では、文字オブジェクトを共用すれば、オブジェクトの数を100個程度に抑えられる

    • ASCIIの7ビット
  • フォントや色の組み合わせもせいぜい10通り程度。これも共用できる

    • 可能な組み合わせの数は膨大だが、常識的に使う分には問題にならない

つかいどころ

下記のすべてを満たすこと

  • 大量のオブジェクトを利用する
  • そのせいで空間コストがかさんでいる
  • オブジェクトの状態のうちほとんどをextrinsicにできる

    • オブジェクトの外に出せる、ということ
  • 状態を外出しすることで、大量のオブジェクトを、比較的少数の共用オブジェクトに置き換えられること
  • アプリケーションが、オブジェクトの一意性に依存していないこと

    • オブジェクトを共用するので、概念的に別のオブジェクトでも等価判定が真になってしまうことがある

      • 例) apple の2文字目と3文字目

登場人物

  • Flyweight

    • 共用されうるオブジェクトのインタフェースを定義
  • ConcreteFlyweight

    • 共用されるオブジェクトのクラス
    • Flyweightの実装クラス
  • UnsharedConcreteFlylweight

    • 共用されないオブジェクト、要するに普通のオブジェクトのクラス
    • Flyweightインターフェースの実装クラス

      • Flyweightインターフェースは、共用を可能にするだけ。強制はしない
      • あとで共用にしたくなったとき、クライアントコードに変更が生じなくなる
  • FlyweightFactory

    • flyweightオブジェクトの生成・管理
    • ConcreteFlyweightの生成・管理

      • Singletonと同様

        • なければ作り、メモ化する
        • メモがあれば返す
    • UnsharedConcreteFlylweightの生成

      • こちらは単に作って返すだけ
    • 【セルフ補足】Clientからは、単にオブジェクトを生成して返すファクトリに見える

      • 特別なファクトリには見えないということ
  • Client

    • flyweightオブジェクトへの参照を保持
    • extrinsicな状態の保持・算出

クライアントコードからの利用

  • flyweightの状態には2種類ある

    • intrinsic

      • flyweight自身が持つ、オブジェクトごとに異ならない状態

        • 文字オブジェクトの文字コード等
    • extrinsic

      • オブジェクトごとに異なるため、外出しされたもの

        • フォント、色、座標等
    • Clientはextrinsicな状態を管理し、必要に応じてflyweightのメソッドの引数に渡す

      • flyweightはもちろんそれを保持してはならない
  • 必ずFlyweightFactoryを通じてflyweightオブジェクトを取得する

    • さもないとオブジェクトが共用されず、2つ目3つ目が生成されてしまう

結果

  • Flyweightパターン適用により、時間のオーバヘッドが生じる

    • オブジェクト自身が持っていた状態を外出ししたため、受け渡し、検索、算出のコストが生じる
    • 空間の節約の埋め合わせである

      • 共用する数が多ければ多いほどペイする
  • 空間の節約は、次の因子によってきまる

    • 共用したことによるオブジェクト数の削減量

      • 多いほど節約になる
    • オブジェクトごとのintrinsicな状態の数

      • 多いほど節約になる
    • extrinsicな状態は、保持されるのか、算出されるのか

      • 算出のほうが節約になる

        • 時間とのトレードオフ
  • Compositeパターンと併用され、ツリーの葉ノードがFlyweightとされることが多い

    • 親ノードへの参照はextrinsicなのでflyweightには持たせられなくなる

実装にあたり考えるべきこと

  • extrinsicな状態を排除すること

    • 空間の節約の見地からいうと、保持するより算出したほうがいい
    • フォントの例では、差分だけを保持し、テキストを走査する際に、副作用を用いて算出するなど
  • flyweightオブジェクトの管理

    • FlyweightFactoryの実装としては、連想配列等が考えられる

      • Clientはキーを指定し、所望のflyweightオブジェクトを取得する
    • 参照カウンタやGCを実装するというアイデア

      • flyweightオブジェクトの数が決まっていて、かつ少数なら、そこまでしなくていい

        • 例) ASCII文字

関連するパターン

  • Composite

    • 一般に、有向非巡回グラフの葉ノードに対して適用できる
  • State, Strategy

    • Stateオブジェクト、StrategyオブジェクトはFlyweightにするとよい

      • ふるまいのみを定義しており、状態を持たないから

英語

  • prohibitively

    • 手が出ないほどに、法外に

      • prohibit — 禁止する、からの派生
      • 高価すぎて実質禁止してる感じ

        • メルカリの99999999円とか
  • inordinate

    • 過度の、法外な

      • 秩序を乱している感じ
  • short of

    • (副詞句)
    • ~まで達さずに、~の手前で

      • さらなる理想形があるが妥協した感じ
  • appreciably

    • 目に見えるほどかなり
  • offset

    • 埋め合わせる
    • 過去分詞もoffset

      • 空間と時間とのトレードオフの文脈で使用
  • reclaim

    • 再利用する

      • オブジェクトを破棄してメモリを再利用する、という文脈で使用
  • excerpt

    • 抜粋