プログラムが動作する上で必須に近い、「boolean」変数。安易に使いすぎると可読性の低下などにより、不利益を被ってしまうことがあります。
フラグの使用例
たとえば、改行コードを含んだ文字列の内容をheader部とbody部に分けるコードを考えてみましょう。
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
String input = "header\nbody1\nbody2\nbody3";
String[] lines = input.split("\n");
String header = null;
List<String> body = new ArrayList<>();
// ヘッダー情報取得済フラグ
boolean flag = false;
for(String line : lines){
// ヘッダー情報が未取得なら、取得してフラグをtrueにする。
if(flag == false){
header = line;
flag = true;
} else {
// 取得済なら、bodyに追加する。
body.add(line);
}
}
System.out.println(header); // -> header
System.out.println(body.toString()); // -> [body1, body2, body3]
}
}
こんなコードが出来上がりました。確かにちゃんと動きます。何が悪いのでしょうか?
フラグを使うべきでない理由
それでは、メソッドの記述に状態変数としてのフラグを使うべきでない理由をいくつか挙げてみましょう。
不要な複雑化を招く
冒頭で挙げたコードは、フラグを使わなくても実現できます。
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
String input = "header\nbody1\nbody2\nbody3";
List<String> lines = Arrays.asList(input.split("\n"));
// 1番上の要素をヘッダーとする
String header = lines.get(0);
// linesをArrayListに変換する
List<String> body = new ArrayList<>(lines);
// ヘッダーを削除する
body.remove(0);
System.out.println(header);
System.out.println(body.toString());
}
}
コードの行数が28行から20行に減りました。コード量が可読性に直結するわけではありませんが、絶対量が減るということは、即ちコードの解読にかかる時間を削減できるということです。
実際の状態との乖離を招く
これは、フロントエンドJavaScriptなどで、INPUT要素を扱う場合に稀に遭遇する現象です。あるチェックボックスに対してフラグをトグルする次のようなコードを考えてみましょう。
var check = false;
document.querySelector("#checkbox").addEventListener("click", function(){
check = !check;
});
これは危険なコードです。このコードは、「チェックボックスがクリックされたらcheck変数をトグルする」だけであって、DOMの内容を確実にcheck変数に反映しているわけではないからです。
たとえば、inputタグにcheckedプロパティが付与されている(最初からチェックされている)だけでも、処理は早々に破綻します。また、他の人がJavaScriptによるDOM操作でチェックボックスのchecked属性を切り替えただけでも、いとも簡単にバグを引き起こす要因となるでしょう。
これらは、変更容易性という点で、到底容認できることではありません。チェックボックスの状態を変更する度に、その状態変数をいちいち書き換えなければならないというようなことは絶対に避けるべきです。
今回のケースであれば、DOM要素への直接アクセスなどでコンポーネントの状態を知ることは容易にできますから、不要な状態フラグを作成しないようにしましょう。
var element = document.querySelector("#checkbox");
console.log(element.checked);
可読性の低下を招く
現代プログラミングの現場では、可読性は最も重要な要素のひとつです。以下のコードを見てください。
public void example(boolean flag){
if(flag){
// ...
}
}
稀に、メソッド引数にbooleanやEnumを取るものがあります。このフラグや識別子に応じて振る舞いを変えているわけですが、違う振る舞いをするメソッドは、そもそも分けるべきです。
public void exampleTrue(){
//...
}
public void exampleFalse(){
//...
}
なぜでしょうか?「名は体を表す」という言葉があるように、メソッドは名前で処理を表すべきです。JavaDocなどのドキュメントをしっかり書いていたとしても、メソッド名に勝る明瞭さはありません。
似たような例として、Java6のLoggerクラスの実例を見てみましょう。これは厳密にはフラグを利用しているわけではありませんが、Enum識別子によって振る舞いが変わるというメソッドがあります。
たとえば、主要メソッドのlog(Level level, String msg)
は、以下のようなドキュメントが公開されています。
log
public void log(Level level, String msg)
引数のないメッセージのログをとります。
しかし、info(String msg)
や、warning(String msg)
などの、リダイレクト的な動作を行うメソッドも用意されています。実際の開発で使うのはこちらでしょう。
info
public void info(String msg)
INFO メッセージのログをとります。
INFO メッセージレベルで現在ロガーが使用可能な場合、指定されたメッセージは登録されたすべての出力 Handler オブジェクトに転送されます。パラメータ:
msg – 文字列メッセージ、またはメッセージカタログのキー
このようにして、メソッドに別口を設けて、より明瞭なインターフェースにする、といったテクニックが使われています。
フラグを使うべきケース
オブジェクトの状態を表すフラグ
たとえば、enabledがそうです。そのオブジェクトが有効かどうかを表しますが、フラグ以外で簡潔な手段を持ちません。これは有効な例です。
しかし、マルチスレッドには注意してください。一連の処理で複数回判定を行うようなメソッドでは、「メソッドの処理途中にフラグが切り替わる」ことに十分留意してコードを組む必要があります。
「synchronized」修飾子は、排他制御を行う有力な手段ですが、本稿の本筋から外れてしまうので、マルチスレッドプログラミングに関しては、今回は割愛させていただきます。
WebAPIインターフェースでフラグを扱う場合
これはアリです。WebAPIに限らず、インターフェース設計はユーザビリティを第一に考え、常に柔軟でなければなりません。例えば、検索APIを作成するときに、一緒に総件数を出力したいが、件数を出力するのにもそれなりのコストがかかります。必要でなければ出力しないほうが良いわけですから、これを出力するフラグをインターフェースとして持つことは何ら不自然ではありません。
しかし、内部実装となってくると話は別で、抽象度の違うクラス(例えば、DAO)にわざわざフラグ情報を橋渡しする必要はありません。そんなものはどこかの層で吸収するべき話であって、最下層の具象メソッドでフラグを取り扱うのは不自然なことです。
とはいえ、どの層でフラグ判断を行うかは、また結論の分かれるところではあります。たとえば、MVCのごくシンプルな開発モデルであれば、フラグを判断する層は「Model」ですし、例えばサービスクラスなどを挟む場合であれば、その責務はサービスクラスが負います。多層アーキテクチャであれば、ビジネスロジック層がその役割を果たします。
データベース上で、データの状態をあらわすカラム
先述した「オブジェクトの状態を表すフラグ」と同じ理由で、データベース設計においても、フラグを設定することがあります。たとえば、図書館などにおける「蔵書」テーブルの「貸出可能」フラグなどが挙げられます。
図書館では、貸出禁止の本が存在する場合もあります。そういった本はデータベース上で貸出可能かどうかを示すフラグが必要ですね。
こういった、「オブジェクト(レコード)の状態そのもの」を表すフラグを設定することは理にかなっています。
まとめ
メソッド中の状態を保持するようなboolean変数は、単にコードの見通しが悪くなるので、利用については注意しましょう。
例えば、for文であれば、現在のインデックスがどの位置にあるかは容易にわかりますし、そうでなくても、フラグなどという陳腐な方法を使わなくても済むケースは意外と多いものです。
ただ、メソッド中でフラグを使ってはならない一番の理由は、「コードリーディング中に、フラグがどちらの状態か常に意識していなければならない」ということです。しかし、この問題が容易に解決できるのであれば、自由に使っても構いません。
現代のプログラミングは可読性が第一です。「フラグを使わないことが正解」ではなく、「読みやすくする」ことを念頭に置いて、コーディングを行いましょう。