TDDとファクトリ系のデザインパターン
ファクトリ関連のデザインパターンに関するポエムです。
まともな技術情報は含まれていません。
ファクトリ系のデザインパターン
ファクトリ系のデザインパターンには複数種類があります。
GOFのデザインパターン本で紹介されているのは下記2つです。
- Factory Methodパターン
- Abstract Factoryパターン
さらにGOFでは紹介されていませんが、より原始的なSimple Factoryパターンもあります。これは上記2パターンのような継承やメタモーフィズムを使わないシンプルなファクトリクラスを使うパターンです。
ファクトリは頻繁に使われるデザインパターンの1つで、プログラマ同士の会話でも「ここはファクトリを使って、、、」みたいな会話はよくあります。
でも私、ファクトリ系デザインパターンを全然、使ってないんですよね。。。
だから会話に入れなくて。。。
使い所がわかってない
私はコーディングするときは事前に分析&設計することはせず、テストが設計を主導するように意識してます。え?ティっ、、TDDの練習です(震え声)。
流れとしては、とりあえずTDDで書き始めます。そしてテストが進んでいく中で試験容易性、DRY原則やSOLID原則等の原則を意識したリファクタリングを実施します。その結果、デザインパターン的なものが生まれることもあります。しかし意識的にデザインパターンを適用することは基本的にしません。
スキルが低く視野の狭い人間が下手に事前設計してデザインパターンを適用するよりテスト主導で設計原則を重視して書いていった方が「マシ」なものができると経験上、思うからです。
この流れでコーディングしているとインスタンスの生成に関しては、呼び出し元でnewで生成して必要な場所にコンストラクタインジェクションで注入する、というワンパターンなやり方でいつも事足りてしまいます。
MainやControllerなど根っこのクラスは注入するオブジェクト数が多くてコンストラクタの引数が多いなー、とか、newしてるからテストやりずらいなー、と感じることはあります。でも根っこのクラスはそもそもテストしずらいものだと思ってますし、一度書いたら終わりなことが多いので、「なにかデザインパターンを適用して改善できるか検討してみよう」という流れになったことは今までありませんでした。
でも私も「ここはファクトリーで」とか言ってみたい!
ということで今回、時間を作って考えてみました。
実験してみよう
テスト主導な設計においてファクトリ系のデザインパターンの適用を検討するタイミングはいつ訪れるのでしょう?
またテストが進んでいく中でどのように各ファクトリ系パターンに発展していくのでしょう?
以降は簡易な実験的なコードでSimple Factory、Factory Method、Abstract Factoryを使ってTDDしてみた感想です。
1. どこでSimple Factoryパターンを適用しようと思うか?
どのファクトリ系パターンに発展する(or しない)にしてもまずは出発点は最もシンプルなSimple Factoryパターンの適用から始まると思います。
ではどのようなタイミングでSimple Factoryパターンを適用する流れになるのでしょう?
デザインパターンを意識してなければ前述のワンパターンでほとんど事足りてしまいます。事足りてるのに適用するのはテスト主導とは言えません。不満が出て初めてその解決策としてSimple Factoryパターンを適用できます。
唯一、不満を感じるのは根っこクラスから注入するオブジェクトが増えてコンストラクタのシグネチャが大きくなったときです。
代わりに複数のオブジェクトを生成するファクトリオブジェクトを一つだけインジェクトするように根っこクラスを変更したらコードがすっきりしました。
興味深かったのはテストでモックしたときです。
テストコードでは継承&オーバーライドでモックしました。プロダクションコードだけ見るとSimple Factoryですが、テストコードも含めて見るとFactory Methodっぽくなりました。
また階層の境界をまたいでSimple Factoryパターンを適用したときは境界なのでInterfaceを用意しました。プロダクションコードだけ見るとInterface付きのSimple Factoryですが、テストコードも含めて見るとAbstract Factoryっぽくなりました。
根っこクラスからnewが排除されました。Mockライブラリを使えばファクトリをMockへ置き換えることは可能です。ただ自分はあまりMockライブラリを使わないのでそれで根っこクラスを試験しやすくなったとは感じませんでした。
2. Factory MethodやAbstract Factoryへの発展
前述の通り、Simple Factoryをテストする過程で自然とFactory Method、そしてAbstract Factoryへ発展します。
今回はシンプルなコードだったのでプロダクションコードだけ見るとFactory MethodやAbstract Factoryに見えませんでしたが、要件が増えてプロダクションコードのクラスが増えてくればそれっぽく見えるようになると思います。
3. 実験の結果
今回の実験を通して感じたのは、TDDの中でもコードスメルを放置しなければ自然にFactory MethodやAbstract Factoryへ発展していくということです。
今までと同じで特にデザインパターンを意識せず、要件とテストと原則に任せればよいと思いました。
その他のコードスメル
先ほどは「コンストラクタの引数が多い」というコードスメルに着目しました。
他にもAbstract Factoryへ発展していく可能性があるコードスメルとして、「同じようなオブジェクト群を沢山生成する」があると思います。 例えば根っこクラスでControllerオブジェクト+UseCaseオブジェクト+Presenterオブジェクト+Viewオブジェクトをユースケースの数だけ生成する場合などです。 根っこクラスが同じようなオブジェクト生成処理で溢れます。 これを多態性を使ってスッキリさせるためにファクトリクラスを作成すればそれがAbstract Factoryパターンへ自然と発展すると思います。
感想
今までファクトリ系デザインパターンとご縁がなかったのはテスト主導だからではなく、コードスメルを放置していたことが原因だと思いました。