Help us understand the problem. What is going on with this article?

プログラムの可読性を上げるための条件分岐を減らす方法7個

受託開発エンジニアでプロジェクトリーダーという立場になるにあたって品質保証について考えるようになりました。

品質と一口に言っても「内部品質」と「外部品質」の2点がありますが、エンジニアである以上、内部品質の向上が外部品質を上げる最適解だと思います。

※内部品質、外部品質についてはこちらを参照。

そして内部品質を上げるためにはアーキテクチャを考えることが大切ですが、既存プロジェクトの場合はアーキテクチャが 存在しないレベルでぐっちゃぐちゃ 十分に検討されていないケースも多いです。

そこで、一番手っ取り早くプログラムの可読性を上げる方法は、新規に書くコードをできるだけシンプルに実装することです。今回はその中でも個人的に使っている条件分岐を少なくする or 見やすくするテクニックについてまとめてみました。

なお、今回の例は「こういうやり方がある」という説明のために利用しているだけで、必ずしもベストな例ではないことをご了承ください。

条件分岐を減らす方法7個

今回の分岐を減らす方法ですが、基準としては

  • 1メソッドあたりの循環的複雑度を下げる
  • ネストを1段階浅くできる

のいずれかを満たしているという条件で考えています。

ガード節

まず、ネストを浅くする最もメジャーな方法としてガード節を思い浮かべた方も多いかと思います。

ガード節とは処理の対象外と条件を、関数の先頭でreturn/continueする方法のことで、その後の処理条件を限定することができるメリットがあります。

まず、悪い例から見ていくと、

sample.php
public function hoge($a, $b) {
    $result = 0;
    if (isset($a)) {
        $result = 1;
    } else {
        if (isset($b)) {
            $result = 2;
        }
    }
    return $result;
}

あまりいい例ではありませんが、このようにifを重ねることによってネストが深くなるケースがあります。これをガード節を書き加えることによって以下のようになります。

sample.php
public function hoge($a, $b) {
    if (isset($a)) return 1;
    if (isset($b)) return 2;

    return 0;
}

最初に $a, $b に値が入っていないかを判定することによって、不要なelseを書く必要がなくなります。
条件分岐を減らしたいと考えたときに真っ先に検討する方法の1つです。

bool判定

続いて true/false 判定をする際にも余計な条件分岐が書かれているケースも少なくありません。
具体的な悪いコードは以下の通りです。

sample.php
public function hoge($a) {
    if ($a === '') {
        return true;
    }
    return false;
}

基本的にtrue/falseを取得したい場合はその条件を変数を返すだけで事足ります。

sample.php
public function hoge($a) {
    return $a === '';
}

配列利用

続いて配列を使って条件分岐を減らす方法もあります。
enumやconfigで定義するほどでもないけど、DBから値を取得して画面でテキストを表示したいといったときに便利です。

では、具体的なコードを見ていきましょう。

sample.php
public function hoge($x) {
    $a = 0;
    switch ($x) {
        case 0:
            $a = '駅名1';
            break;
        case 1:
            $a = '駅名2';
            break;
        case 2:
            $a = '駅名3';
            break;
    }
    return $a;
}

決して悪い書き方ではありませんが、配列を使って以下のように書き換えることができます。

sample.php
public function hoge($x) {
    $array = [
        0 => '駅名1',
        1 => '駅名2',
        2 => '駅名3',
    ];
    return $array[$x];
}

こちらの方がシンプルでいいですね!

仮にswitch文に「defaultに入ったときは0を返す」といった条件があったときもnull合体演算子を使ってシンプルに書くことができます。

こちらはあくまでもPHPの例なので他の言語では上手く動作しない可能性があります
@Ask79273103 さん、ありがとうございます。

sample.php
public function hoge($x) {
    $array = [
        0 => '駅名1',
        1 => '駅名2',
        2 => '駅名3',
    ];
    return $array[$x] ?? '駅名0'; // $xが'駅名1', '駅名2', '駅名3'でないときは$array[$x]がnullのため'駅名0'が返される
}

メソッド分割

続いて根本的な解決ではありませんがメソッドを分割して、1つのメソッドあたりの循環複雑度を減らす方法があります。
例えば以下のようなコードがあったとします。

public function hoge($array) {
    $result = [];
    foreach ($array as $item) {
        if ($item) {
            array_push($result, $item);
        }
    }
    return $result;
}

このif文を別メソッドに切り分けることでコードの可読性を上げることができます。

sample.php
public function hoge($array) {
    $result = [];
    foreach ($array as $item) {
        $this->fuga($result, $item);
    }
    return $result;
}

private function fuga(&$result, $item) {
    if ($item) {
        array_push($result, $item);
    }
}

正直この例ではあまりメリットが分かりづらいですが、例えば「ある特定の条件結果がtrue/falseによって処理を分ける」といったケースでは可読性が上がるケースがあります。

sample.php
// 変更前
$result = '';
if ($condition) {
    if (...) {
        foreach (...) { // TODO }
    } 
} else {
    foreach ($array as $item) {
        // TODO
    }
}

// 変更後
$result = $condition ? $this->hoge() : $this->fuga();

条件分岐を減らすテクニックというよりは、きちんとモジュール化しましょうという話なので少し毛色は違いますが立派なテクニックと言えるかと思います。

三項演算子の活用

人によって意見が分かれますが三項演算子を使うのもネストを浅くする1つの選択肢になります。
具体的に以下のコードを見ていきましょう。

sample.php
$result = 0;
if ($condition) {
    $result = 1;
} else {
    $result = 2;
}

三項演算子を使えば以下のように書くことが可能です。

sample.php
$result = $condition ? 1 : 2;

ただし三項演算子は読み手によってはわかりにくいと言われることも少なくありません。
したがって、三項演算子を使う場合にも複雑な処理を書かないことは頭に入れておく必要があるかと思います。

型定義の実装

また、型定義が行える言語であるにも関わらず型を定義していないという場合もあるかもしれません。
例えば、PHPは7系から型定義ができますし、TypeScriptなどでもanyをできるだけ使わないようにすることで分岐を減らすことができます。

sample.php
public function hoge($id) {
    if (!is_int($id)) {
        throw new \Exception('id must be type int');
    }

    $sql = "SELECT * FROM user WHERE user_id = {$id}";
    ...
}

型定義は以下のように実装することができます。

sample.php
public function hoge(int $id) {
    $sql = "SELECT * FROM user WHERE user_id = {$id}";
    ...
}

型定義によってどれだけエラー発生が減らせるか?については以下のスライドも参考になります。
PHP7 で堅牢なコードを書く - 例外処理、表明プログラミング、契約による設計

言語特有の演算子活用

最後に言語特有の演算子を活用したり言語特性を生かすことによって条件分岐を減らすことができます。
例えばPHPであれば、null合体演算子を活用して以下のようにコードを変更することができます。

sample.php
// 変更前
public function hoge($a) {
    $result = 0;
    if (!is_null($a)) {
        $result = $a;
    }
    return $result;
}

// 変更後
public function hoge($a) {
    return $a ?? 0;
}

他にもJavaScriptでは以下のように書くことができます。

sample.js
// 変更前
const hoge = value => {
    let result = 0;
    if (!!value) {
        result = value;
    }
    return result;
}

// 変更後
const hoge = value => {
    return value || 0;
}

追記
@aliyome @shogogg さんにコメントをいただきましたが、正しくnull/undefined判定を行うためには以下が正しいコードになります。

sample.js
// 変更後
const hoge = value => {
    return value ?? 0;
}

JavaScriptは0やfalseなどもfalsyであり、それらも含めて判定を行いたい場合には上記の論理OR演算子 || を活用してください。

このように言語特性を生かすことによってネストを浅くする方法がないか?は一度探してみてもいいのではないかと思います。

他にもこんなテクニックあるよ!という方は、ぜひ追記したいのでコメントをお願いします!

ddtaka
大阪でエンジニアをしています。普段はReact Native書いたりLaravel書いたりとフロントエンドとバックエンドを行ったり来たりしています。新しい技術も好きですが、新しくて便利なサービスも大好きです。
https://memorandumrail.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした