この記事は私が本を読み進めるたびに追記されます
メタ設計パターン
以下のような問が発生したときの回答となるようなメタ設計パターンがGoFによって述べられている。
問
- どのように他のオブジェクトと関連するのか
- それらはどのように結びつくべきなのか
- それらは互いに何を知るべきなのか
- 頻繁に変わりそうな部品を入れ替えるにはどうすればいいか
メタ設計パターン
- 変わるものを変わらないものから分離する
- インターフェイスに対してプログラミングし、実装に対して行わない
- 継承より集約
- 委譲、委譲、委譲
また、ラス・オルセンのアイディアとして
- 必要になるまで作るな
Template Method
あなたのコードは44行目を除いて全く同じことをさせたいのかもしれません。44行目はこっちをしたいときもあるし、あっちをしたいこともあります。そのような時は Template Method が使えます。
基底クラスに普遍の部分を記述し、変わる部分はサブクラスに定義するメソッドにカプセル化します。基底クラスはメソッドを未定義にしておくことができます。ただしその場合はサブクラスでそのメソッドを提供しなければなりません。
用語
- フックメソッド
- Template Methodの具象クラスによってオーバーライドできる非抽象メソッドのこと
- サブクラスが使うことができるメソッド。具体的な意味を持つので、非抽象と言えるのかな。
- ダックタイピング
- JavaやC#ではインタフェースが決められていて、このクラスはどのインタフェースを持つか。それらの型はなにか。が定義される。Rubyの場合は、クライアントが呼び出そうとしているメソッドをオブジェクトが実装していることだけをチェックする。これをダックタイプと呼ぶ
ダックタイピングできることは危険なことか?
静的型付けのほうが安心に思えるかもしれない。
現実的に、周りに出てきた言語は静的型付けを行うことができる言語も多い。当たり前のように静的型付け言語を使用しているが、見逃している点がある。それは「静的型付けのコスト」である。馬鹿にならないはず。
Rubyによって書かれたRuby on RailsやRubyの標準ライブラリは現実に存在し、それが問題なく動いていることが動的型付け言語でも問題が起きないことを示している。
実例
-
Webrick::GenericServer
を継承するHelloServer
-
initialize
メソッド
Strategy
ひょっとすると変わるのは44行目ではなくてアルゴリズム全体なのかもしれません。ちゃんと仕事は定義されていて、完了させる必要があるのですが、方法が沢山あるのです。猫を檻から出す仕事があったとき、それには1つ以上のテクニックがあるかもしれません。それらのテクニックやアルゴリズムをラップするのが、 Strategy オブジェクトです。
Template Method と解決したいことは同じ...?
Template Methodは問題解決のために、継承の上に成り立っている。そのため、たとえどんなに注意深くコードを設計しても、サブクラスはそのスーパークラスに依存してしまいます。一度、アルゴリズムを選択すると方針を変えるのが困難になり、レポートフォーマットを変更する必要がある場合、わざわざ具象クラスを作る必要がある。単に 出力形式を切り替えたいために...
GoFのメタ設計パターンでは 継承より集約 と述べている。サブクラスをたくさん作る代わりに、たくさんの変化に富んだコードの塊をすべて引き剥がしてクラスに閉じ込めてしまうのはどうだろう。
GoFではコンテキストと呼ばれる
クラスの外部からはストラテジオブジェクトが同じように見えることから、ストラテジの利用者(GoFではコンテキスト(Context)と呼んでいます)はそれらを取替可能なパーツとして扱うことができるようになります。
ストラテジオブジェクトを使う側を、コンテキストと呼ぶ。なぜなら、ストラテジオブジェクトは同じ仕事をこなすだけではなく、正確に同じインタフェースを提供するため、外側から見た場合、どんなストラテジオブジェクトがDIされても、気にしない。
Strategyパターンの現実的なメリット
関心の分離をとてもうまく達成することができます。Strategyパターンを使うことで、一切の責務と知識を、
Report
クラスから取り除くことができるのです。
Template Methodと共通すること
バリエーションの決定を1箇所ないしは数箇所に集中できるという点。
- Template Methodの場合は、具象サブクラスを選択するとき
- Strategyの場合は、実行時に注入するストラテジオブジェクトの選択するとき
コンテキストとストラテジ間でのデータを共有する方法
2つある
- ストラテジオブジェクトにメッセージを送るときに、ストラテジ内部で必要なデータを引数として設定して送る方法
- ストラテジオブジェクトにコンテキストオブジェクト自身を送る方法
ダックタイプ
「抽象」Formatter基底クラスと、HTMLFormatterとPlainTextFormatterの2つの派生クラスから構成していました。しかしながら、この方法はかなり「Ruby的ではない」手法です。
どちらの派生クラスも、同じインタフェースを共有していれば、それは同じである...というダックタイプの哲学で言えば、別に基底クラスを保つ必要はない。一般にRubist達は基底クラスをすっ飛ばすやり方を好みがち