導入
前回作成したビンゴマシンを使ってみましたか? 音や画面のエフェクトが全くありませんから、
さて、
展開
制御フラグの削除
参考文献に挙げている
制御フラグの削除
(Remove Control Flag) 論理型の制御フラグでコードの流れを制御すると、
読みにくいコードになりやすい。その場合は制御構造の構文(break,return)を使って制御フラグを削除する。
Remove Control Flag
今回作成したビンゴマシンのコードでは、TEST_
というフラグを使用しています。リファクタリングの意図は,、
今回は、
- 問題の制御フラグが使用されている箇所をすべて見つける。
- この制御フラグが使用されている箇所のうち一ヵ所を、
breakで置き換える。 - テストを実行し、
この置き換えで問題が発生しないか確認する。 - 問題がなければ2.に戻る。すべての箇所を置き換える。
- 制御フラグを削除する。
練習として次のコードで考えてみます。このような小さなコードの場合には制御フラグによる分岐や条件判断も
boolean done = false;
int i = 0;
while( ! done ){
if ( i < 10 ){
i++;
println("i = " + i);
} else {
done = true;
println("done!");
}
}
このコードに対して作業すると次のようになります。
int i= 0;
while( true ){
if ( i < 10 ){
i++;
println("i = " + i);
} else {
break;
}
}
println("done!");
注意したいことは、
では、BingoMachine.
から制御フラグを削除するべきでしょうか。コードの一部を見てみましょう。
BingoMachine.pde
の一部static final boolean TEST_MODE = true; //テストモード
//static final boolean TEST_MODE = false; //アプリモード
void setup(){
if (TEST_MODE == true) {
runTest();
} else {
runBingoMachineApp();
}
}
制御フラグを用いて、setup
メソッドだけではなく、draw
, mousePressed
といったシステムから呼ばれるメソッドにも同様の構造があります。
BingoMachine.
に現れる制御フラグTEST_
はstatic final
で宣言された定数で、TEST_
を削除し、break
やcontinue
、return
といった制御構造命令で置き換えることでコードはシンプルになりません。かえって複雑になりそうです。
Processingには今のところJUnitのようなテストフレームワークがありません。ですから、
ここで一つの教訓として覚えてもらいたいのは、
クラスの抽出
ビンゴ数列を発生して、
ファウラーはこのリファクタリングを
クラスの抽出
(Exctact Class) 一つのクラスが責任を多く負いすぎている場合、
まとまりのあるフィールドやメソッドを切り出して別のクラスにする。
Extract Class
現状のビンゴマシンで機能が足りて、
そこで、
まずはこの程度の定義で考えてみます。next
, hasNext
を実装し、remove
は実装しないでおきましょう。remove
が呼ばれたら、UnsupportedOperationException
を返すようにします。
リファクタリングの実施
今回のリファクタリングは
テストの作成
リファクタリングのはじめに、testBingoProgressionClass
メソッドの行を追加します。
void runTest(){
println("runTest called.");
createBingoProgression(bingoProgression);
assert testBingoProgression(bingoProgression) == true
: "ビンゴ数列にもれかだぶりがあります" ;
assert testBingoProgressionClass() == true
: "ビンゴ数列発生クラスにエラーがあります"; // 追加された行
println("runTest finished.");
}
そして、testBingoProgressionClass
メソッドを次のように実装します。
testBingoProgressionClass
メソッドの実装boolean testBingoProgressionClass(){
println("testBingoProgressionClass called.");
BingoProgression bp = new BingoProgression();
ArrayList <Boolean> listForCheck
= new ArrayList<Boolean>();
for( int i = 0; i < BINGO_MAX_NUMBER; i++ ){
listForCheck.add(false);
}
for( int i = 0; i < BINGO_MAX_NUMBER; i++ ){
Integer currentElement = bp.next();
println("["+(i+1)+"] = " + currentElement);
if ( listForCheck.get( (int)currentElement -1 ) == true ){
return false;
} else {
listForCheck.set((int)currentElement-1,true);
}
}
println("testBingoProgressionClass finished.");
return true;
}
テストに合格したら、
なお、testBingoProgression
メソッドのコードと重複部分が多いので、
演習
演習1(難易度:middle)
BingoMachine.
にリファクタリングBingoProgression
クラスを抽出しましょう。リファクタリングの前後でアプリケーションの振る舞いに違いを生じさせないでください。
まとめ
- リファクタリング
「制御フラグの削除」 と 「クラスの抽出」 を学習しました。
学習の確認
それぞれの項目で、
- 「制御フラグの削除」
の意図と効果が理解できましたか? - 理解できた。
- 意図は理解できたが、
効果を感じない。 - 理解できない。
- 「クラスの抽出」
の意図と効果が理解できましたか? - 理解できた。
- 意図は理解できたが、
効果を感じない。 - 理解できない。
参考文献
- 『新装版 リファクタリング―既存のコードを安全に改善する―
(OBJECT TECHNOLOGY SERIES)』(マーチン・ ファウラー 著、 オーム社) - かつてピアソン・
エデュケーション社から出版されていたものの新装版です。リファクタリングのバイブルですから、 必携です。なお、 本文中に示した引用のページ位置はピアソン・ エデュケーション社版のものです。
- かつてピアソン・
- 『Java言語で学ぶリファクタリング入門』
(結城浩 著、 ソフトバンククリエイティブ) - ファウラーのバイブルと併せて読むことをお勧め。著者の解説は一級です。
演習解答
リファクタリングを施したコードを次に示します。
実装を簡略化するために、
定数 BINGO_
を共有せず、MAX_ NUMBER それぞれのコードで定義しました。先々、 さらに大きな数や小さな数のビンゴに対応するためには、 主となるコード側でのみこの定数を定義し、 BingoProgression
クラス側へはコンストラクタの引数で渡すようにすればさらにコード変更に対応しやすくなります。あるいは各クラスで共通して利用する定数をstaticなフィールドとして持つクラスを用意しても良いでしょう。また、
ビンゴ数列のテストコードは、 せっかく Iterator
インターフェイスを持つBingoProgression
クラスを使っているのに、従前のfor文のままのコードです。ここは拡張for文を使えばコードがシンプルにならないでしょうか。ロジックを少々変更する必要がありますが、 取り組む価値がありそうです。 では、
主となるsketchの mousePressed
メソッド内のビンゴ数列要素取得コード部分はいかがでしょうか。ここもIterator
インタフェイスのメソッドを呼んでいます。Iterator
インターフェイスのオブジェクトを作成してアクセスするほうが良いですか? 無駄ですか? 応用課題として検討してみましょう。