Factory Methodについて勉強してみる

はいどーもデザイナーを目指すニートです。
本日はデザインパターンの一つであるFactory Methodについて勉強していきたいと思います。
あ、言語はC#です。

Factory Methodってなに

GoFのデザインパターンの一つです。
newでインスタンスを生成するかわりに、ファクトリメソッドを使ってインスタンスを生成しましょう、というやつです。
これの何が嬉しいかと言えば、newでインスタンスを生成すると、その生成元のクラスとインスタンスが強く結びついてしまうのですが、ファクトリメソッドを使うと柔軟に生成できるというわけです。
例えば派生クラスがたくさんあって、そのどれかを生成するか動的に決めたい場合なんかにいいんじゃないでしょーか。

これの何が役立つのかわからなかった

「派生クラスがたくさんあって、そのどれかを生成するか動的に決めたい場合なんかにいい」とは書いたのですが、自分で書くクラスでそういう書き方をしたいと思ったことはわりと少なく、むしろファクトリ作るのめんどくさいしもうnewでいいやってなることさえありました。
つまりわざわざファクトリを作るメリットをあまり理解できていませんでした。
ですが現在読んでいる本に「これは明確なメリットだ」と感じるものがあったので、今回はそれを紹介してみようと、そういうわけなんですね。

テストしたいがコンストラクタ内でオブジェクトを生成している場合

テストしたいクラスがあるとします。
ですがこのクラスはコンストラクタ内でオブジェクトを生成し、そのオブジェクトがテストハーネス内でおこなうべきではない処理を実行しているとします(例えばDBへの接続)。
このときnewを使ってインスタンスを生成していると、もうDBへの接続はどうしようもありません。
ですがファクトリメソッドを使っているならば、テストしたいクラスのサブクラスを作成し、ファクトリメソッドをオーバーライドすればそれで事足ります。

サンプルコード

説明だけではナンノコッチャ感があると思うので、簡単なサンプルコードを載せます。

これですが、バナー型の広告をロードするていのコードです。
この広告は一定時間で消え、現在の広告が表示されている時間を取得できるとします。
で、広告は「表示されていた時間」と「タップ回数」でポイントが貯まっていくとします。
それを計算するためのクラスに以下のようなものがあるとします。

注目してほしいのはコンストラクタです(計算方法は適当なので気にしないでください)。
コンストラクタ内でBannerAdのインスタンスを作成してしまっているため、AdPointCalculaterとBannerAdが強く結びついてしまっています。
もしAdPointCalculaterのCalculateメソッドをテストしたいと思ったとき、このままではテストのたびにサーバーに接続してしまうことになります。
これは嫌ですね。

テストしやすくする方法

テストを簡単にするためには、いくつかの方法があるかと思います。
その一つは今回の記事の題材でもあるファクトリメソッドを使う方法ですね。
つまりAdPointCalculaterを以下のような感じにします。

BannerAdはIBannerAdを実装することとします。
このようにしておくと、サブクラスでCreateBannerAdをオーバーライドでき、簡単にAdPointCalculaterのテストが簡単におこなえるようになります。
例えば以下のような感じ。

数値は適当なので、テストに合わせて色々と調整する必要がありますが、これでサーバーに接続しなくてもテストができるようにはなりました。
あとはテストハーネス内でTestingAdPointCalculaterのインスタンスを使えばOKです。
一歩前進ですね。

もう一つの方法

依存性の注入をおこなうことでも対処できます。
要はこんな感じ。

変わったのは、コンストラクタでIBannerAdインターフェイスを受け取っていることだけです。
たったこれだけですが、AdPointCalculaterのインスタンスを生成するとき、BannerAdクラスに依存しなくなっています。
例えばテストするときにはコンストラクタにTestingBannerAdクラスのインスタンスを渡せば、先程のファクトリメソッドの例と同じようにテストができるようになります。

まとめ

ファクトリメソッドを使うか、それとも依存性の注入を使うかは、既存のコンストラクタの複雑さにもよるのかなーと思いました。
例えばコンストラクタが複雑でかつ、すでに多くのクラスで使用されているなら、依存性の注入よりもファクトリメソッドの方法の方が簡単に実装できるのではないかなと思いました。
逆にコンストラクタが単純で、まだそれほど多くのクラスで使用されていないなら、依存性の注入の方がお手軽かなーと感じました。

まあ何にせよ、テストしたいクラスがDBにアクセスしたり何やりするなら、具象クラスに依存するのはちょっとよろしくない気がしますね。
インターフェイスを使ってあげるのが色々優しいかなーと思いました。

終わりに

実は昨晩熱が40度を超えていたのですが、今日は37度台と落ち着いています。
この調子なら明日か明後日には全快するんじゃないかなーと思っています。
というわけで久々の勉強記事を終わりたいと思います。
ほなそんな感じでまた。

あ、お仕事も募集中です。

フォローする