デザインパターン学んで得られたこと

キーワード駆動アプローチのテストシステムを作る上でその構造について悩んだ時に読んだこちらの書籍と、そこから学んだことを受けて考えを巡らせた時のことについて書きます。

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

デザインパターンの書籍を読んだ感想

テストシステムを構造化、アイディアを形に(実装)する上での良い参考例になりました。
特に「保守性」や「分かり易さ」が鍵になると考えていた次の要件を実現することができるようになったと思います。

  • 継続的にメンテナンスしながら使用していきたい
  • 他の人にもメンテナンスしてもらいながら利用してもらいたい

デザインパターンから具体的に恩恵を受けた点

具体的には次のような点でデザインパターンの恩恵を受けることができました

  • 自分のプログラムを構造化できた
  • 再利用しやすい構造にリファクタリングできた
  • JUnitといったOSSのソースを読めるようになった(コードの意図を理解できるようになった)
  • アプリケーション全体を設計する手法を知らないことにも気づけた

おかげで、OOADについて知り、ある方の紹介でユースケース駆動設計についても知る機会を得ることが出来ました。
jugemix.hatenablog.com


ICONIX」では、デザインパターンは詳細設計フェーズでオブジェクト間の相互作用を構造化する一つの手法として紹介されています。
設計について勉強しようと思っている方は、デザパタの勉強から始められると、プログラムの構造設計からアプリケーションの構造設計へと思考を広げられるようになるので良いと思います。

実際に役立ったデザインパターン

ここからは実際にOSSの中で見られたデザインパターンや、テストシステムを構築する際に実際に役立ったパターンを具体例と合わせてまとめます。

チェーンオブレスポンシビリティ

目的の処理を持つオブジェクトをチェーンのように実行していく。JUnit4.x系ではStatementクラスとして採用されているパターンである。

次のソースのmethodBlockメソッド(281行目参照)を参照いただきたい。
github.com
ここでは、Statement抽象クラス型のstatementオブジェクトに実行するオブジェクトRunBefores/RunAfters等のオブジェクトが追加されていることに気づくと思う。

テンプレートメソッド

抽象メソッドで構成する処理の流れを具象メソッドにまとめ、抽象メソッドの実装はサブクラスに任せる。
これにより、具象メソッドで処理の全体の流れは強制しつつ、具体的な処理内容は個別の実装に任せたい時に有用。
共通の前処理と後処理の間に個別の処理を実装したいーといった時に効果を発揮する。

例えば、チェーンオブレスポンシビリティで紹介したBlockJUnit4ClassRunnerクラスは実際にインスタンス化されるランナーであるが、この継承元のParentRunner抽象クラスではこのStatementを実行するrunLeaf具象メソッド(321行目参照)が実装されていて、Statement自体の作りは各具象ランナーに委譲しているテンプレートメソッドパターンとなっていることが読み取れると思う。
github.com

少し脱線しますが、JUnitを使ったテストでテストの実行順や必要な処理を追加するような場合にカスタムランナーを作られると思いますが、その場合は、このStatement型オブジェクトに意図した順に実行されるロールのオブジェクトを追加することが妥当なランナーのカスタマイズ方法だと私は思います。
これを実践しているのが、PitaliumのPtlBlockJUnit4ClassRunnerWithParametersクラスですので、参照されると良いでしょう。
github.com

ファクトリーメソッド

オブジェクトを生成する側のクラスでnewをしないでオブジェクトを生成する。

一般的なプログラミングの入門書では、生成する側のオブジェクトが生成される側のオブジェクトをnewするような記述になっている。
しかしこの場合、生成される側のクラス名等に変更が発生した場合、生成する側のオブジェクト格納先、そのオブジェクトを利用するコードにも影響が発生するがある。

そこで生成する側のオブジェクトで、生成されるオブジェクトのインタフェースを受けとるようにファクトリーメソッドを記述したらどうだろうか。
インタフェースを実装するクラスに変更があっても生成する側には、生成する箇所にしか影響が及ばないようになる。
インタフェースを介しているので、インタフェースオブジェクトを利用するコードには影響が及ばないのである。

このようなことからファクトリーメソッドを利用すると、生成されるオブジェクトと、オブジェクトを生成する側のオブジェクトの結合度を緩やかにする効果がある。
WebDriverのPageFactory.initElementsはページオブジェクトを生成する際の良い例である。
github.com

ブリッジパターン

機能のクラス階層が実装のクラス階層のインスタンスを保持し、役割を保持する構造のパターン。
機能の追加、実装の追加を同時に行うことができる。
f:id:jugemix:20161229141714p:plain

コマンドパターン

処理をオブジェクト化することで、その処理のメンテナンス性と拡張性の両方を兼ね備えることができる。
f:id:jugemix:20161229141707p:plain

その他役立ちそうなデザインパターン

その他にも役立ちそうだと思ったパターンを簡単にまとめます。

イテレータ

対象データをコレクションとして扱い拡張for文を使うことで、そのコレクションに格納しているデータを繰り返し操作できるようになる。
データを保管するクラスを作る時に、イテレータを実装するとデータの取り扱いが容易になることがある。

アダプター

既にあるAPIを利用して、必要とするAPIを作ることができる。
無理にオーバーライドせず、必要とするクラスを作ることができる。

シングルトン

インスタンスを複数作らず、使い回したいときに有効。リソースを節約できる。
PitaliumのWebDriverはWebDriverManagerシングルトンクラスにより、テスト間で使い回し可能な作りになっている。

ステート

状態を具象クラスとして扱う。そのクラスが状態に関する共通の処理をインタフェースとして実装する。
この結果、状態に応じて処理を呼び出す側は、状態インタフェースを実装したクラスのオブジェクトを保持することで、現在の状態を気にする必要がなくなる。

まとめ

一般的に知られているデザインパターンを知り使うことで、誰にでも分かりやすいコードが書けるようになることが分かったかと思います。
今後、詳細設計を行う際には、このようなパターンの適用も視野に入れて開発に取り組んでいきたいと思います。