#1. 今回お話しすること
-
「メンテナンスしやすいコード」という軸でまとめたよ
-
Javaやjavascriptコードを想定して説明を書くけど、全言語共通で使えるテクニックだよ
-
私個人の考えなので、「いいな」と思うものだけピックアップしてもらえたら何よりです
-
今回は「フラグ」という軸でいろいろと語るよ
-
今回の話は「0/1で考えるべき値」なのか「1/2で考えるべき値」なのかという軸で話します。言い換えれば「booleanで持つべき値」なのか「enum、char、0でない数値で持つべき値」なのかとも言えます。以下の話では0/1と1/2という数字表記で統一をします。
#2. テクニック集
##2.1. 0 or 1 (=true or false)でない変数に「~flag」という名称を付けない
ちょっと想像してみてください。
これから皆さんは、あるタスク(ToDo)を管理する機能を作ります。
この各タスクについて、完了状態(「完了」「未完了」)をあらわす場合、どんな変数を定義するでしょうか。
また、各タスクの緊急度(「緊急」「普通」)をあらわす場合、どんな変数を定義するでしょうか。
これら2つの変数は、必ずこの2つの値しか持たせないものと仮定します。
// 完了フラグ
boolean completeFlag;
// 緊急フラグ
boolean emergencyFlag;
んー、まぁこんな感じですかね。
ではここで仕様が変わって、緊急度は「高」「普通」「低」の3段階にしたいと言われたらどうでしょう?
この変数名のまま実装を開始しますか?
フラグとはそもそも「1ビットで表現できる情報を保持する変数」と言われています。「~flag」という名称では、どうしても2択という先入観を持ってしまいます。この先入観が、急いでいるとき、焦っているときの実装ミスにつながりかねません。勘違いが生まれやすい名称は控えましょう。こういった仕様変更があったのであれば(まだ変数名変更してもプロジェクトに大きな影響がないのであれば)、この機に名称を変えるべきだと思います。例えば「emergencyLebel」とか「emergencyType」とかですかね?
さて、このような3択以上の選択肢がある場合に~flagという名称を使わない、というのは皆さん何となくわかるかと思います。
では1 or 2の2択の場合はどうでしょう?これは意見が分かれるのではないでしょうか。
個人的な感覚では、1 or 2の2択の場合はflagという名称や、boolean型を使うべきではないと思っています。次節で続けたいと思います。
※コメントにてenumを使うというご指摘をいただきました。ありがとうございます!
もちろん最終的なゴールとしてenumは当然あるのですが、この記事ではまずは0/1なのかそうでないのかを深く掘り下げたいと思います。
##2.2. 0 or 1の変数と1 or 2の変数の考え方の違い
さて1節では「完了フラグ」「緊急フラグ」というものを例に説明しました。
そこで皆さん、問題です。
「性別」はどういう名称にしますか?また各々どういった論理式、数値を想定しますか?
まあこれは「gender」だとか「sex」だとか、そういった名前にすることが多いでしょう。maleFlagとか、femaleFlagとかという名前を付けようという場面は少ないはずです。
では、genderという名称を付けた場合、値は 0 or 1にしますか?1 or 2にしますか?
個人的には、「1 or 2」かなと思っています。
ではなぜ0 or 1はダメなのでしょうか。(というか僕が生理的に受け付けないのでしょうか。)
この0/1の変数と、1/2の変数の考え方の違いを僕なりに考えてみました。
この2つの違いは、大きな枠でいうと以下の2点かと思います。
・2つの選択肢が同格であるかどうか
・どちらかに値が偏っていないか(偏る想定がないか)
システムの世界では0というのは特別な存在です。Javaのintでいえば初期値は0になります。何もしなくても0という数字が入ってくるのです。つまり0/1でいう0と1は、「何もしていない」のと「何かをした」という大きな違いがあります。
こういう考え方でいうと、冒頭のToDoでいうところの「完了フラグ」はまさにこの考え方ですね。タスクが発生したとき(=まだ何もしていないとき)は0、完了させたのなら1が立つという設計です。
ところが男女というのは、同格の存在です。また(基本的には)五分五分で存在しています。男を0にするということは、「だいたい男性だね」とか「まずはみんな男性でスタートするよね」というような意味がちらついてしまいます。逆も然りです。そういったところが「完了フラグ」と「男女」の捉え方の違いなのかなと考えています。
つまり、フラグというと、普通でない状態に印をつけるとか、初期状態と比べて変化したところに印をつけるといった印象が強いもので、選択肢のそれぞれがフラットであったり、全くベクトルの違うものであればフラグという名前を付けるべきでなく、0/1でない値を定数として持ったほうがいいと思っています。
ちなみに、システムによって考え方が変わる可能性があります。例えば結婚している家庭をターゲットにしたサービスで、利用者の99%の人が結婚しているようなサービスがあったとします。そういった場合は「普通は結婚している」という考え方のもと、既婚を0、未婚を1という定数にするかもしれません。作るシステムの特性によって、臨機応変に変える必要があると思っています。
##2.3. 「種別」「状態」など0 or 1でない数値を入れる変数を作る際、各定数をどう割り振るか検討しよう
さて、これまで3択で考えてきた緊急度ですが、リリース後にまた仕様変更が入りました。
3段階ではなくもっと多く10段階くらいで管理したい、と。
皆さんは慌てます。
「え、-1, 0, 1で表現してきたけど(enumでいうならLOW, NORMAL, HIGHですかね)、-5~5とかするの(11段階になるけど)?え、これまでの3段階は-3, 0, 3くらいな意味合いで使いたい?にゃんですと?」
でも、こういった重要度とかって、段階を増やしたいという展望は十分想像できますよね。このように今後選択肢が増えうる変数を扱う場合、連続した数字を定数に振るのではなく、間を空けて設計しておくという手法があります。
例えば最初の3段階の設計時に -100, 0, 100という定数を振っておきます。そうすると-10, -20というように増やしても行けますし、-200, -300というように増やしてもいけます。(enumを使う場合にLEVEL_0, LEBEL_1みたいなパラメータにしてると同様の事件が起きますね。)
もちろんこのような間を空ける設計をすると、「全部で何種類選択肢があるのか」というのがわかりにくくなります。また初見でこの数字を見た時に「ん?」と固まってしまう方もいるでしょう。
パッチをあてれば済むことではありますが、実際のユーザのデータをUPDATEするという行為は神経を使います。こういったことをなるべくしないようにするため、各々の組織の考え方や風土に応じて、設計段階でわかるものについてはこのような検討もしてみるといいのではないでしょうか。
最後に、ここでは数値で話をしましたが、実際のコードではbooleanを使うことが多いでしょう。1/2もenumを使うことが多いかと思います。
二択の変数を考えるときに、booleanがいいかな?それとも違うものがいいかな?という判断のイチ材料になれば幸いです。