ブログトップ 記事一覧 ログイン 無料ブログ開設

Strategic Choice

2013-05-23

[][]戦術編:手法:メソッドのパラメータ化

メソッドのパラメータ化

f:id:asakichy:20130416133933p:image

どういうこと?

メソッド内で生成しているオブジェクトを、メソッドのパラメータで渡せるように変更します。元のシグネチャを持つメソッドも作成しておきます。

f:id:asakichy:20130416133929p:image

どうして?

メソッドでパラメータを渡せるようになれば、テストに適したオブジェクトを外部で生成し、それを使ってテストが実行できるようになります。

どうすれば?

「メソッドのパラメータ化」は以下の手順で行います。

  1. 置き換えたいメソッドを特定して、そのコピーを作る。
  2. メソッドのパラメータに、生成処理を置き換えたいオブジェクトを追加する。オブジェクトの生成処理を削除し、パラメータからオブジェクトを保持する変数への代入処理を追加する。
  3. コピー元のメソッドの本体部を削除して、パラメータ化したメソッドを呼び出すようにする。元のオブジェクトはそのメソッド内で生成する。

本日の格言

多くの言語では、シグネチャが異なれば、同じ名前の複数のメソッドを1つのクラスに持たせることができる*1。これにより、メソッドのパラメータ化を行った場合の作業がかなり簡略化される。

ただし、同じ名前のメソッドが増えるので、混乱を招くこともある。これを避ける方法は、新しいメソッドの名前にパラメータの型を含めることだ。*2

*1:メソッドのオーバーロードのことです。

*2:先述の例でいえば、「run」は元のメソッドの名前として残し、新しいメソッドの名前を「runWithTestResult」とします。

2013-05-22

[][]戦術編:手法:コンストラクタのパラメータ化

コンストラクタのパラメータ化

f:id:asakichy:20130416133754p:image

どういうこと?

コンストラクタ内で生成しているオブジェクトを、コンストラクタのパラメータで渡せるように変更します。元のシグネチャを持つコンストラクタも作成し、その処理は新しいコンストラクタに委譲します。

f:id:asakichy:20130416114333p:image

どうして?

コンストラクタでパラメータを渡せるようになれば、テストに適したオブジェクトを外部で生成し、それを使ってテストが実行できるようになります。*1

どうすれば?

「コンストラクタのパラメータ化」は以下の手順で行います。

  1. パラメータ化したいコンストラクタを特定して、そのコピーを作る。
  2. そのコンストラクタのパラメータに、生成を置き換えたいオブジェクトを追加する。コンストラクタ内でのオブジェクト生成処理を削除し、パラメータからインスタンス変数にオブジェクトを代入する。
  3. 古いコンストラクタの本体を削除して、新しいコンストラクタの呼び出しで置き換える。古いコンストラクタ内では、new演算子を使ってオブジェクトを生成して新しいコンストラクタを呼び出す。*2

本日の格言

「コンストラクタのパラメータ化」は、とても簡単で、頻繁に使うリファクタリングだ。

コンストラクタにパラメータを新しく追加したことにより、クライアントに対して、そのパラメータのクラスに対する依存関係を新たに導入してしまうが、そんなことは些細な問題である。

*1:デフォルト引数をサポートしている言語であれば、そちらを使う方が簡単です。ただし、C++でこれを行う場合、ヘッダーファイルをインクルードする必要があります。この点で、デフォルト引数はあまりお勧めできません。

*2:利用している言語で、コンストラクタから別のコンストラクタの呼び出し可能な場合です。呼び出せない場合は、コンストラクタ間の重複を新しいメソッドに抽出したほうがよいかもしれません。

2013-05-21

[][]戦術編:手法:リンクによる置き換え

リンクによる置き換え

f:id:asakichy:20130416112344p:image

どういうこと?

置き換えたい関数と同じシグネチャの関数を持つ、テスト用ダミーライブラリを作ります。テスト時は、ダミーライブラリとリンクします。

f:id:asakichy:20130416110930p:image

どうして?

オブジェクト指向言語が持つポリモーフィズムは、C言語のような手続き型言語では使用できません。

「リンクによる置き換え」を行うことで、ソースコードを変更することなく、関数を置き変えることが可能になります。関数以外に、ファイルやグローバル変数などにも応用可能です。

どうすれば?

「リンクによる置き換え」は以下の手順で行います。

  1. 擬装したい関数を特定する。
  2. それらの代替関数を作成する。
  3. 本番用ではなく、代替関数をインクルードするようにビルドを調整する。

本日の格言

「リンクによる置き換え」は、実は、オブジェクト指向言語でも適用可能である。

たとえば、Javaであれば、依存関係に問題があるクラスに対して、同じ名前とメソッドを持つクラスを作り、それを呼び出すように「CLASSPATH」を変更すればよい。

2013-05-20

[][]戦術編:手法:静的setメソッドの導入

静的setメソッドの導入

f:id:asakichy:20130416102848p:image

どういうこと?

Singletonに静的setterメソッドを導入して、疑似インスタンスを注入できるようにします。

インスタンスを置き換えるための静的setメソッドを追加します。保持しているインスタンスがfinalであれば、それを外します。コンストラクタの可視性はprotectedにします。

f:id:asakichy:20130416104825p:image

どうして?

テストの妨げになっているSingletonのインスタンスを入れ替えることができます。Singletonのサブクラスを作って、新しいオブジェクトを生成し、それをsetメソッドに渡します。

どうすれば?

「静的setメソッドの導入」は以下の手順で行います。

  1. Singletonクラスのコンストラクタの保護レベルを下げる。
  2. Singletonクラスに、静的setメソッドを追加する。そのsetメソッドでは、Singletonクラスへの参照を受け取るようにする。また、そのsetメソッドでは、新しいオブジェクトを設定する前にSingletonオブジェクトを確実に破棄する。
  3. テストの準備のために、Singletonのprivateメソッドやprotectedメソッドにアクセスする必要がある場合には、Singletonをサブクラス化するか、インタフェースを抽出してそのインタフェースを実装するオブジェクトの参照をSingletonで保持するようにする。

本日の格言

Singletonクラスの保護レベルを下げることを躊躇してはいけない。この処置によって、テストが増え、「より良い状態」に向かうのは確かだ。

そして、究極の「より良い状態」は、Singletonへのグローバルな参照を減らして、通常のクラスに変更できることである。

2013-05-17

[][]戦術編:手法:インスタンス委譲の導入

インスタンス委譲の導入

f:id:asakichy:20130415224622p:image


どういうこと?

staticメソッドについて、同機能のインスタンスメソッドを導入します。

インスタンスメソッドは、staticメソッドに処理を委譲するだけの実装になります。また、インスタンスメソッドのアクセス指定子は、オーバーライド可能なものとします。

f:id:asakichy:20130415224621p:image

どうして?

staticメソッドはオーバライドできません。そこで、同等の機能をもつインスタンスメソッドに差し替え、オーバライド可能な設計に切り替えます。

つまり、「インスタンス委譲の導入」を使用すると、オブジェクト接合部を得られる、ということです。

どうすれば?

「インスタンス委譲の導入」は以下の手順で行います。

  1. テストにおいて問題があるstaticメソッドを特定する。
  2. 特定したメソッド用のインスタンスメソッドをそのクラスに作成する。その際には、シグネチャを維持しておく。インスタンスメソッドでは、処理をstaticメソッドに委譲する。
  3. テストで保護したいクラスの中で、そのstaticメソッドを呼び出している場所を見つける。
  4. 元のstaticメソッドの呼び出しを、委譲用のインスタンスメソッドの呼び出しで置き換える。

本日の格言

staticメソッドだけのユーティリティクラスが、「インスタンス委譲の導入」によって、いつの日か、すべての呼び出しが委譲メソッドを経由するようになるかもしれない。

その時こそ、staticメソッドの本体をインスタンスメソッドに移動して、staticメソッドを削除する時だ。