1. 導入:なぜ循環的複雑度が重要なのか
プログラミングにおいて「コードが複雑である」とは、具体的に何を指すのでしょうか。その客観的な指標の一つが「循環的複雑度(Cyclomatic Complexity)」です。この数値は、プログラムの制御フロー(if文やループなどの分岐)がどれだけ多いかを示します。数値が高いほどテストケースの数が増え、バグが混入しやすくなります。本稿では、この複雑度を意識し、モダンなJava機能を使ってコードを簡潔に保つ手法を解説します。
2. 基礎知識:循環的複雑度とは
循環的複雑度は、プログラムのグラフ理論に基づいています。単純に言えば、「ifやswitch、forなどの分岐が1つ増えるごとに、複雑度も1増える」という考え方です。
例えば、メソッド内にif文が5つあれば、そのメソッドのテストには最低でも6通りのパスを網羅する必要があります。複雑度が高いコードは「認知負荷」が高く、修正時に予期せぬ副作用を生む原因となります。
3. 実装・解決策:分岐を減らすモダンな書き方
Java 17以降の「Sealed Classes(封印されたクラス)」や「Switch Expressions」を活用することで、従来のif-elseの連鎖を排除し、構造を整理できます。
特に、データ型に応じた処理を行う際にinstanceofの連鎖を避ける「パターンマッチング」を使うことで、複雑度を劇的に下げることが可能です。
4. サンプルプログラム
以下の例では、従来のif-elseによる複雑な構造を、Switch Expressionsを使って整理した例を示します。
// 従来の書き方(複雑度が高い)
public String getStatusOld(Object obj) {
if (obj instanceof String) {
return “文字列です”;
} else if (obj instanceof Integer) {
return “数値です”;
} else {
return “不明”;
}
}
// モダンな書き方(Switch Expressionsとパターンマッチング)
public String getStatusNew(Object obj) {
// switch式を使うことで分岐をフラットに表現できる
return switch (obj) {
case String s -> “文字列: ” + s;
case Integer i -> “数値: ” + i;
case null, default -> “不明な型”;
};
}
5. 応用・注意点:現場での運用
現場で循環的複雑度を管理する際は、以下の点に注意してください。
・しきい値を決める: 一般的にメソッドの複雑度が10を超えると「警告」レベルとされます。SonarQubeなどの静的解析ツールを導入し、CI環境で自動チェックするのが定石です。
・早期リターン(Guard Clauses)の活用: ネストが深くなるif文は、条件を満たさない場合に即座にreturnすることで、コードのインデントを浅く保てます。
・ポリモーフィズムの検討: もし一つのメソッド内で巨大なswitch文が発生しているなら、それはクラスの責任範囲が広すぎるサインです。Strategyパターンなどのデザインパターンを用いて、処理をクラス単位で分割することを検討してください。
複雑度を意識することは、単なるコードの綺麗さだけでなく、将来の自分やチームメンバーが「安心して変更できるコード」を残すための重要な投資です。

コメント