GoF本 Chapter 2 WIP

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

Chapter 2 A CASE STUDY:DESIGNING A DOCUMENT EDITOR

ドキュメントエディタ「Lexi」を題材に、

  1. さまざまな設計上の困難と
  2. それを克服するために使用されているデザインパターン

を紐解いていく

2.2 Document Structure

Problem

  • LexiはWYSIWYGな感じの文書エディタ

    • MS Wordみたいな感じか
  • 編集対象の文書はさまざまな要素からなる

    • テキスト
    • 画像
    • 基本図形
  • テキストを特別扱い、画像を特別扱い、とかはしたくない

    • さもないとしくみが複雑になるから

Solution: Composition Pattern

  • テキスト、画像、基本図形など、「目に見えるなにか」をGlyphとして汎化
  • 目に見えない「行」「段組み」といったものもGlyph派生

    • 「行」に相当するRowクラスはGlyphを集約する … 再帰的な木構造

2.3 Formatting

  • Formatting: 文書の成形方法

    • どこで改行するか、とか

Problem

  • Formattingの頭の良さや美しさは実行速度とのトレードオフ

    • WYSIWYGなので、実行速度も重要
  • Formattingのアルゴリズムには、以下のものがあるとする

    • Array
    • TeX
    • Simple
  • 段組みColumn : Glyphをまとめ、文書全体を表現するComposition : Glyphクラスがあるとする
  • 各アルゴリズムに対応するCompositioin派生を作りたくなる:

    • ArrayComposition
    • TexCompositon
    • SimpleComposition
  • Formattingは良し悪しなので、ユーザが実行時に選択できることが望ましい

    • 上記の継承では実現できない

Solution: Strategy Pattern

  • アルゴリズムを汎化し、別の継承ツリーにはじき出す

    • 文章をComposeする抽象クラス、Compositorを定義
    • アルゴリズム別の具象クラスを用意

      • ArrayCompositor
      • TeXCompositor
      • SimpleCompositor
    • 僕的には後置修飾のほうが好み
      — 入力補完や、並んだ時の美しさの面で

      • CompositorArray
      • CompositorTeX
      • CompositorSimple
  • Compositionクラスは、Formattingアルゴリズムに対応するCompositorインスタンス_compositorをもつ
  • Formattingアルゴリズムを選択するには、_compositorインスタンスを挿げ替える

    • Compositor派生は状態をもつべきではない
  • Compositionは文書をFormatする際、_compositor.Compose()に処理を委譲する

2.4 Embellishing the User Interface

文書に以下のものを加えたい

  • 枠線
  • スクロールバー

いずれも、文書そのものとは関係ない飾りつけである (embellishment)

Problem

  • 飾りつけの処理の追加により本命の処理に影響を与えたくない
  • クラス爆発は勘弁

    • BorderedComposition とか BorderdCompositionWithScrollBar とか悪夢

Solution: Decorator Pattern

  • 本命の処理を行うクラス(Composition : Glyph)を、同じインタフェース(Glyph)で包んでやり、処理を追加する
  • 同じインタフェースGlyphで包むので、Glyph利用側からは包む前後で区別がつかない (Transparent Enclosure)
  • いま、Glyphをちょうど1つ集約するGlyph派生、Monoglyphを定義する
    枠線追加クラスBorder、スクロールバー追加クラスScrollerの定義は下記のようになる

    • Monoglyph : Glyph

      • Border : Monoglyph
      • Scroller : Monoglyph
  • 下記のように集約させると、文書にスクロールバーがつき、その周りに枠線がつく
Border <>-- Scroller <>-- Composition <>-- ...
  • もしも、枠線ごとスクロールしたければこう。クラス爆発も回避できる
Scroller <>-- Border <>-- Composition <>-- ...
  • Composition以降の集約関係や、GlyphとしてCompositionを利用していたコードには変更が一切生じない

2.5 Multiple Look-and-Feel Standards

Problem

  • プラットフォームによって、ボタンやスクロールバーなど(ウィジェットと総称)の外観と雰囲気が異なる
  • ユーザーを混乱させないためには、プラットフォーム標準に合わせたい
  • すぐ思いつくのはこのような実装
    各ウィジェットの各プラットフォーム版クラスを作り、利用側でnewする
ScrollBar* sb = new MacScrollbar();
Button* btn = new MacButton();
...
  • 良くない点

    • 他プラットフォームへの移植が大変
    • 不注意でWindowsButton()とかが混ざる可能性がある

Solution: Abstarct Factory

  • ScrollBarとかButtonとか、ウィジェット一式を作るファクトリクラスGUIFactory をつくる
  • プラットフォーム別に具象クラスをつくる

    • GUIFactoryMac : GUIFactory
    • GuiFactoryWindows : GUIFactory
  • ウィジェット生成コードはこうなる
GUIFactory* guiFactory = new GUIFactoryMac();

ScrollBar* sb = guiFactory->CreateScrollBar();
Button* btn = guiFactory->CreateButton();
...
  • Windows版にしたくなったらこう
- GUIFactory* guiFactory = new GUIFactoryMac();
+ GUIFactory* guiFactory = new GUIFactoryWindows();

ScrollBar* sb = guiFactory->CreateScrollBar();
Button* btn = guiFactory->CreateButton();
...
  • GUIFactory*は、プラットフォーム判明後、ウィジェット生成ならどこで設定してもいい(実行時に選択できる)

2.6 Supporting Multiple Window Systems

Problem

  • ウィンドウはプラットフォームにより仕様が大きく異なる -グラフィック描画

    • 最小化・元に戻す
    • 座標系
  • 各プラットフォームのウィンドウに対して、下記のバリエーションがある

    • アプリケーションのメインのウィンドウ
    • ダイアログ
  • プラットフォーム別ウィンドウの派生を作るのは良くない

    • サブツリーをビルド時に切り替えないといけない
![20181130235519](../../../imgs/20181130235519.png)
antipattern: プラットフォーム別ウィンドウを派生
  • フラットにするのはもっとよくない(AppMacWindowとか)

    • クラス爆発

Solution: Bridge Pattern

  • プラットフォーム別実装を別の継承ツリーに切り出す

    • WindowImp抽象クラスとその派生
    • 具象クラスをインスタンシエートするには、AbstractFactoryをつかう
  • Windowの継承ツリーは、あくまで「メイン」「ダイアログ」といった「種類」に使う