hirapi's blog

ちゃんとしたふりをする

GoFのデザインパターンをなんとなく納得する

いつか使いこなす日を夢見て、簡単にぐぐってふーんと思ったことをメモした。
ふと思い出したらラッキーという程度。

参考

techracho.bpsinc.jp

ja.wikipedia.org

www.techscore.com

作成系

  • 🔍「オブジェクトを作成する」という行為をクラスとして抽出している
  • 🙈 ありがち
    • クライアントがなんでも作る = クライアントがオブジェクトに詳しい
    • 別々のクライアントで似たような生成処理がコピペされている
  • 🤔 複雑なインスタンス生成がそのへんに転がってたら見直すべきかも

Simple Factoryパターン

Factory Methodパターン

  • Template Methodパターンと一緒に使われるらしい
    • 手続きを抽象クラスで定義して、具象クラスで各メソッドを実装するというやつ
  • インタフェースAの実装クラス(複数あり、どれを使うかをクライアント側は知らない)のインスタンス化を、インタフェースBの実装クラスのFactory Methodが行う
    • インタフェースAの実装クラスのうちどのクラスのインスタンスを作るべきか、インタフェースBの実装クラス達だけが知っている
  • Factory MethodパターンはFactory Methodを持つ抽象クラスと実装クラスの関係
  • Template Methodの中でインタフェースAの実装クラスをインスタンス化する必要があるとき、インタフェースBの実装クラスのFactory Methodに任せる

Abstract Factoryパターン

  • 複数のクラスのインスタンス化をFactoryクラスにまとめる
    • a factory of factories
  • Abstract Factoryパターンはクライアント側とFactoryクラスの(オブジェクト同士の)関係
    • 「複数のクラスをひとつずつインスタンス化する」という行為を、クライアントからFactoryクラスに移譲する

Builderパターン

  • Foo クラスのインスタンス作成をFooBuilder#build に任せる
  • Builderクラスのメソッドでオブジェクトを作り、クライアントに返す

Prototypeパターン

  • インスタンス化のコストが高いとき、元になるオブジェクトをcloneして新しいインスタンスを作る
  • 💬 使うタイミングがイメージできない

Singletonパターン

構造系

  • 🔍 インタフェースの取り付け方のパターン
    • インタフェースをつける・統一する
      • クライアントから使いやすくする(クライアントの知識を減らす)ため
      • メモリを節約するため(クライアントの仕事を減らす)ため
    • そもそも何をインタフェースとして切り出すか
  • 🙈 ありがち
    • is-aでないのに、メソッドを使いたいために継承する
    • 個別のクラスになんでも実装する
    • 別々のクラスに同じメソッドをコピペする
  • 🤔 Fat Modelや謎継承になりそうだったら見直すといいかも

Adapterパターン

  • 既存のクラスをAdapterでラップしインタフェースを変更する
  • 💬 予期していなかった変更への対応手段?

Bridgeパターン

  • クラスの階層に2つの継承関係があるとき

    ja.wikipedia.org

    • DishwareからPlate・Bowl
    • PlateからWoodPlate・GlassPlate、BowlからWoodBowl・GlassBowl
  • 継承関係を分け、1つを別のクラスに移譲し、注入する

Compositeパターン

  • 木構造を伴う再帰的な構造の中で、葉ノード(Leaf)と内部ノード(Composite)を同一のものとして扱う
    • ファイルとフォルダを区別しないようなケース
    • LeafとCompositeに共通のインタフェースがある
  • CompositeはLeafやCompositeの集合を持っており、自分以下の階層に再帰的に処理を行うメソッドを持っている
    • ルートノードで一度実行すると、Leaf・Compositeの区別なく前ノードに処理が進んでいく

Decoratorパターン

  • もとのオブジェクトをDecoratorクラスでラップし、ふるまいを拡張する
    • もとのオブジェクトをDecoratorクラスのコンストラクタで注入する
    • もとのクラスとDecoratorクラスでインタフェースを共有させ、クライアントから同じように扱えるようにする
  • もとのクラスを継承してクラスを作るよりも動的に変更できる
    • 💬 あるインタフェースを持つべきオブジェクトと無くていいオブジェクトが存在しているとき?

Facadeパターン

  • クライアントと複雑なサブシステムの間で窓口になり、クライアントにシンプルなインタフェースを提供する

Flyweightパターン

  • 使い回すオブジェクト群をFlyweight Factoryクラス内で(配列・マップとして)保持しておき、同じオブジェクトが必要になったときに新しくオブジェクトを作るのではなく保持されたものから返す
  • イミュータブルなオブジェクトを使い回すのに使う

Proxyパターン

  • 別のオブジェクトのインタフェースとして機能する
  • Flyweightパターンと組み合わせて、一つのオブジェクトを元に複数のインタフェースを取り付けることもできる(らしい)

振る舞い系

  • 🔍 オブジェクト同士の通信について、よくある関係のための実現パターン
    • 🔍 関係性・通信をクラスに抽出する
  • 🙈 ありがち
    • クライアント(コントローラ的なもの)で全部やる

Chain Responsibilityパターン

  • 自分のうしろに続く同じ処理オブジェクトを1つ持つ
  • 自分が処理できなかった場合、うしろのオブジェクトに処理を渡す
    • 受け取った次のオブジェクトも、自分が処理できなかったらうしろに渡す
  • 処理の責任を次々に渡していく

Commandパターン

  • 処理に必要な細かい動作・パラメータをCommandオブジェクトに閉じ込める
  • クライアントから呼ばれたInvokerがCommandを介してReceiverを操作する
  • 向いている処理
    • undo、トランザクション
    • 実行せずに処理(順)をためておき、一気に実行するとき
    • 他のものにも同じ処理をさせたいとき

Iteratorパターン

Mediatorパターン

  • Mediatorインタフェース
  • Mediator実装クラス
  • Colleagueクラス:Mediatorを介して通信する人々
  • 💬 クラスにバリエーションが無くてもインタフェースを定義するの、制約を明確に表すためという理解でよいんだろうか(このパターンに限らず)
    • Colleagueクラスの中でMediatorクラスのメソッドを呼ぶので、インタフェースで明確にしておく必要がある、ということか

Mementoパターン

  • ある時点へのロールバックを可能にする
  • Originator:何らかの状態を持つ
    • Memento:状態を保存したもの
  • Caretaker:Mementoを保持しておき、保存したりロールバックしたりする

Observerパターン

  • Subject:イベントを通知する
    • SubjectにObserver群を登録する
    • Observerに通知を送る( Observer#notify を呼ぶ)
  • Observer/Listener:通知を受け取る
    • インタフェースで notify() の実装を強制されている

Visitorパターン

  • Visitインタフェース: visit() を持つ
  • Elementインタフェース: accept() でVisitインタフェースを持つオブジェクトを受け取る
    • 複数のElementを要素に持つコンテナに適用するアルゴリズムをVisitとして分離するパターン
    • コンテナ側を変更せずに処理を追加できる
  • Elementの実装クラスを構成要素として持つコンテナがある
  • Elementクラス
    • accept(Visitor) を定義し、その中で visitor.visit(self) を実行する
  • Visitorクラス
    • ElementクラスA, B, ...に対してそれぞれ visit(ElementA) visit(ElementB) のように定義し、要素ごとにアルゴリズムに沿う処理をする
    • コンテナ全体を受け取るメソッド visitContainer(Container)も用意し、その中でコンテナの各要素にアクセスしてそれぞれに Element#accept(self) を呼ぶ
  • Elementクラスから成るコンテナ
    • accept(Visitor) を定義しの中で visitor.visitContainer(self) を呼ぶ
  • 💬 accept(Visitor) はVisitorの持つアルゴリズムの処理が適用可能だよということか
    • コンテナ側・要素側はアルゴリズムの詳細を知らないが、自身に適用できることは知っている
    • アルゴリズム側は要素にどういう処理をするか、実装クラスそれぞれについて決めている

Strategyパターン

  • Strategy:データ構造を受け取り、何らかの処理をする
  • Context:Strategyを保持する
  • クライアントはContextからStrategyを取得し、データを渡して処理をさせる

Stateパターン

  • Stateインタフェースが強制するあるメソッドについて、Stateクラス毎に実装が異なる
  • Contextが状態(= Stateクラスのオブジェクト)を持ち、状態を更新したり、その時のStateのメソッドを呼んだりする
  • 💬 Strategyとの違いは状態の更新の有無?
    • 1つのContextの中で状態が変わっていくのか、戦略によって複数のContextがあるのか
  • 💬 solidusでOrderの状態によって挙動が違うメソッドあった気がする

Template Methodパターン

  • 抽象クラスでTemplateMethod(ステップの実行順を決めているメソッド)を定義し、具象クラスで各ステップを実装する
  • ステップ中でオブジェクトを生成するところがあったら、Factory Methodパターンで使うクラスを分けたりする