GoF本 Bridge

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

ねらい

  • 種類に関する継承ツリー(Abstraction)と実装に関する継承ツリー(Implementor)を分離する

    • 種類に関する継承ツリー

      • Window
      • IconWindow
      • DialogWindow
    • 実装に関する継承ツリー

      • WindowsWindowImp
      • MacOSWindowImp

別名

Handle/Body

モチベーション

  • 種類と実装を一つの継承ツリーに盛り込むとクラス数が爆発して大変なことになる

    • 種類を追加するたびに実装派生クラスを追加しないといけない

      • TransientWindowsWindow
      • TransientMacOsWindow
    • 実装を追加するたびに種類ぶんだけクラスを追加しないといけない

      • CentOSWindow
      • IconCentOsWindow
      • DialogCentOsWindow
  • 種類(Abstraction)と実装(Implementor)を分離し、別々に拡張できるようにする

つかいどころ

  • 種類の抽象化と実装とを結合させたくない

    • 実行時に選択したり挿げ替えたりしたい
  • 種類と実装とを別々に拡張したい
  • 実装が変わってもクライアントコードに再コンパイルが生じないようにしたい
  • 【C++】ヘッダ定義中のprivateメンバを隠したい

    • Pimplイディオム
  • クラス数が爆発してやばい

    • nested generalizations
  • 実装を複数オブジェクト間で共有し、かつクライアントコードからそのことを隠蔽したい

構造

20181215114748

登場人物

  • Abstraction

    • 種類に関する抽象化を行うクラス
    • Implementorオブジェクトへの参照を保持する
    • 冒頭の例でいうところのWindow
  • RefinedAbstraction

    • Abstractionの派生
    • Windowの例でいうところのIconWindowとか
  • Implementor

    • Abstractionが仕事をぶん投げる先のクラス
    • Abstractionのインタフェースと対応している必要はない

      • Implementorのインタフェースは低レベル

        • 「線を描く」
        • 「円弧を描く」
      • AbstractionのインタフェースはImplementorのものに基づいた高レベルなもの

        • 「棒人間を描く」
  • ConcreteImplementor

    • Implementorの実装クラス

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

  • クライアントはAbstractionのメソッドを呼ぶ
  • AbstractionImplementorに処理を委譲する

結果

  • インタフェースと実装とが分離される

    • 実行時に挿げ替え可能
    • コンパイル時の依存が断たれる

      • ConcreteImplementorの実装に変更があっても、Abstractionやクライアントコードに再コンパイルが生じない
    • システムがいい感じにレイヤ分けされる

      • 上の層が細かいことを知りすぎない
  • 拡張性の向上

    • AbstractionImplementorとの継承ツリーを独立して拡張できる
  • 実装の隠蔽

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

  • Implementorが1つしかないパターン

    • degenerated Bridge
    • 無意味…ではない

      • ConcreteImplementorに変更があってもAbstraction以上のレイヤには再コンパイルが生じない
      • 【C++】Pimplイディオム

        • ヘッダ中のprivateメンバを隠したい
  • 「正しい」ConcreteImplementor実装クラスを、いつどうやって決めるか

    • Abstractionが決める

      • 「要素数」を受け取り、少なければ2分木、多ければHashとか
      • cf. Strategy Pattern
        「少なければバブルソート、多ければマージソート」とかはStrategy
    • Abstract Factoryに決めさせる
  • Implementorオブジェクトの共有

    • 参照カウントを搭載し、解放忘れを回避、といったこともできる

      • Bridgeパターン関係なくね??
    • C++11で追加された、shared_ptr<T>みたいなかんじ

      • shared_ptr<T>に関してはDecoratorパターンな気もする

        • T*としての透過性を維持しつつ「参照カウントが0になったら解放」という機能を付与している
  • 多重継承

    • C++などでは、Abstractionをpublicで、ConcreteImplementorをprivateで多重継承することができる
    • が、ConcreteImplementorと密結合してしまうのであらゆるメリットが失われる

関連するパターン

  • Abstract Factory

    • Implementorの生成
  • Adaptorとの対比

    • Adapterは、Bridgeの登場人物でいうと、既製Implementorを後付けでAbstractionで包む感じ
    • Bridgeは、設計段階でAbstractionImplementorを前もって用意しておく
  • Strategyとの対比 (ぼく解釈)

    • BridgeパターンにおけるImplementorは状態込みでの「実装」
    • StrategyパターンにおけるStrategyは状態をもたず、ロジックのみ

      • 状態はAbstractionがもつ
      • 状態を持たないので、Flyweightと併用できる

英語

  • gear toward

    • ~に合わせて調整する
  • up-front(形、副)

    • 前もって(の)、あらかじめ(の)