Factory Method
目的 FactoryMethod はインスタンスの生成に関するパターンです。 このパターンの目的は、インターフェイスに対する実装の生成・代入局面を隠し、疎結合を完成させることです。
概要 インターフェイスを用いてポリモーフィズム(多様性)を実現したいとします。また、利用側にインターフェイスだけを見せることで疎結合も実現したいとします。こうすることで、多様な実装の切り替えに対応することができ保守性を高められるからです。さて、この時気をつけなければならないのが、ポリモーフィズム適用時の隙(仮称)です。
ポリモーフィズム適用時の隙 あるサスペンスドラマで犯人が怪人の仮面をかぶっていたとします。犯人はAさんかも知れない。しかしBさんかも知れない。AさんにもBさんにも第1の犯行が可能だ。そして難解なことに第2の犯行も可能だ。
ポリモーフィズムとは「多様性」です。仕事を決めてやり方を決めない「インターフェイス」によってその多様性を担保します。多様性はインターフェイスを満足させられる実装であれば、どれでも実現可能…という仕組みで担保します。この例で説明するならば、犯人はAさんでもあり得るしBさんでもあり得るのという部分です。そして「疎結合」というのは、利用者は何を使っているのかを感知していないという構造で担保します。この例で説明するならば、目撃者は仮面をつけて行った2つの犯行しか見ていないという部分です
しかし、AさんかBさんの何れかが仮面を被るその瞬間を見られていたら?
多様性はいとも簡単に崩れ去ります。ですから犯人は仮面を被る瞬間を見られてはいけません。仮面を付けた状態で物陰から登場するのです。この、物陰の役割を果たす存在こそが Factory Method、物語や設計をミステリアスにさせるエッセンスです。
以上の例からもわかるように、Factoryと聞くと生成プロセスに注目してしまいがちですが、FactoryMethodの真の目的は生成物をインターフェイスの型で提供することで、その実体を隠しきることなのです。
例えば、良く見かける生成プロセスを隠しきれていないコードを提示します。このコードは生成のプロセスを完全に隠蔽してはいません。そのため当該スコープにおける多様性は担保出来ていません。これが Factoryを使わないコードの限界です。
var e tech.Engineer = &impl.Onda{} e.Program() e.Test() e.Publish() ではこのコードが問題であるかというと、一概にそうとも言えません。何故なら代入の瞬間以外はインターフェイスに依存していないコードであることは担保できているので、本当にポリモーフィズムが必要になったその後に、簡単にポリモーフィズムを完成させることができるからです。
[余談] ポリモーフィズムとインターフェイス分離の原則 先の例に関してですが、名探偵が、様々な状況から推測して犯人を絞り込んでゆく様は、インターフェイスの責務を増やしてゆく過程に相当します。犯行日の深夜に動けた人、凶器を調達できた人、そして被害者の行動を知っていた人。最終的にインターフェイスを満足させ続けた人が犯人です。この様にして重すぎるインターフェイスの責務は実現クラスの枯渇を招きます。謎を解き明かしたい探偵の技術としては良いものですが、抽象化の力を振るいたい設計者として悪しきものです。この件に関する詳細は「インターフェイス分離の原則」で説明したいと思います
実現 次のシナリオでは、"Hello"という文字列を格納するため、Storageを生成しています。最初のテストケースでは factoryを用いてその実体を隠蔽できていますが、二番目のテストケースでは factoryを用いないため、その実体を隠しきれていません。この瞬間に疎結合という価値は失われてしまいます。
package factory_method_pattern import ( "testing" "github.com/stretchr/testify/assert" "github.com/koooyooo/go-design-pattern/factory_method/factory" ) // FactoryMethodを適用した場合 // Storageの実体を知ることなく利用できる (疎結合) func TestFactoryMethod(t *testing.T) { // Factory経由で実体を隠蔽 s := factory.CreateStorage() err := s.Store([]byte("Hello")) assert.NoError(t, err) } // FactoryMethodを適用しない場合 // Storageの実体を知ってしまう (密結合) func TestNonFactory(t *testing.