数週間ほど前、コミットをみていたら「ひっかかる」APIがありました。それについてコメントし、わかりやすいAPIについてやりとりしました。そのやりとりの中で、設定ファイルの項目名をわかりやすくするために、いままであまり意識せず工夫していたことに気付きました。それは「設定ファイルの項目名を否定形で修飾しない」というものです。「否定形」という言い回しではなく、「ネガティブな単語を使う」というような言い回しの方が近いような気がしますが、あまりしっくりくる言い回しを思いつかないので、ここでは「否定形」ということにします。
それでは、そのときのやりとりと明文化された工夫を紹介します。
きっかけとなったdiff
きっかけとなったのは次のようなdiffです。
bool config_is_disable_preview (Config *config);
+bool config_is_hide_preview (Config *config);
bool config_is_enable_rss_feed (Config *config);
disableとhideはどう使い分けているのか?is_disable_XXXは英語としてみたときに不自然じゃないか?などいくつか気になる点が複数の人から挙がりました。その中で、「disable」と「enable」という反対の意味の単語を使ったAPIが並んでいることにひっかかりました。そこで、まずはどうしてひっかかったかを整理しました。
disable/enableにひっかかった理由
disableとenableだけみると反対の意味と分類できます。そこにhideも加えるとdisable/hideが否定形で、enableが肯定形と分類できます。否定形の表現が混ざっていることがひっかかった理由でした。
否定形が混ざっていると、コードを読むときに次のように考えるため、ひっかかってしまいます。肯定形に揃っていたほうが読むときにスムーズに読みやすくなります。
disableなAPIでifを書くと次のようになります。
if (!config_is_disable_preview(config)) {
/* 有効なときの処理 */
}
これを読むときのことを考えます。
if (!config_is_...
このあたりまで見たときは、最初に「!
」があるので、「あぁ、否定がついているから無効なときだろう」と勝手に推測して、次のように考えています。
if (!config_is_...) {
/* 無効なときの処理 */
}
その後、
if (!config_is_disable_...
あたりまでくるとdisableが見えるので「disableの否定だからenableで有効なときか」と次のように考えます。
if (!config_is_disable_...) {
/* 有効なときの処理 */
}
実際はここまで意識していませんが、どう考えているだろうと振り返ってみるとこう考えていそうです。一度推測が外れていることがひっかかると感じるポイントなのでしょう。
ひっかからなくする
ひっかかるポイントがわかったので解決策を考えます。
ひっかかるポイントは「否定のときに真を返す関数」があることです。これをなくせばひっかかるポイントをなくせます。つまり、真偽値を返す関数をすべて肯定のときに真を返すように統一すればよいのです。
is_disableはis_enableに、is_hideはis_showに、といった具合です1。
どうして否定形を使っていたか
このようなやりとりの中で、「そもそもどうして否定形のAPIにしたか」の理由を教えてもらいました。理由は「設定ファイルの項目名とあわせるため」ということでした。この理由はプロダクト全体での統一感があがるので妥当な理由といえます。
設定ファイルでは次のような設定項目となっていました。
disable_preview = true
hide_preview = true
enable_rss_feed = true
では、どうしてこのような項目名にしたかというと「デフォルト値を変える場合に設定を書くから」ということでした。
デフォルトでプレビュー機能は有効なので、プレビュー機能を使うために設定ファイルに何かを記述する必要はありません。設定ファイルに記述するときはプレビュー機能を無効にするときだけです。そのため、「disable_preview」という設定ファイルになっているということでした。これもそれっぽい理由に思えます。しかし、APIの統一感を損なっている(否定形と肯定形が混ざっている)のが実情です。
では、どうすればよいでしょうか。それを考えたとき、あまり意識せず設定項目の名前のつけ方を工夫していたことがわかりました。だいぶ長い前ふりでしたね。
設定項目の名前のつけ方
否定形の設定項目名にならないように、次のように設定するように工夫していました。
preview = disable
rss_feed = enable
項目名にenable/disableなどの情報を入れずに、項目名は設定対象だけにし、値を真偽値にします。場合によっては「disable/enable」だけではなく「yes/no」や「true/false」も受け付けるようにします。
preview = false
rss_feed = yes
そして、デフォルト値をコメントとしてデフォルトの設定ファイルに書いておくようにします。
# preview = enable # <- デフォルトで有効な場合
# rss_feed = disable # <- デフォルトで無効な場合
こうすれば、設定を変更しようと設定ファイルを確認したとき、どのような値にしなければいけないか迷わずに済みます。現在の挙動が設定ファイルにコメントとして書かれているので、その項目のコメントを外し、反転すればよいことを推測できるでしょう。
milter managerの設定ファイルでは、次のようになっています。デフォルトでデーモン化しないので「false」を設定する行がコメントになっています。
# manager.daemon = false
今回のpreviewのようにdisable/hideと複数の設定項目がある場合はグループ化します。このとき、enabled/visibleというように肯定形を使います。
パターン1:
preview.enabled = true
preview.visible = true
パターン2:
[preview]
enabled = true
visible = true
肯定形に統一できたので、APIを設定項目に合わせてもAPIを肯定形に統一できます。例えば、config_preview_is_enabled()
となります。
APIと同じように設定項目も否定形と肯定形がまざっているとひっかかってしまいます。そのため、設定項目がAPIと関連付いていない場合でも肯定形に統一することはわかりやすい設定項目を作ることにつながります。
まとめ
diffを見て気になったAPIについてやりとりすることで、統一感のあるわかりやすい設定項目にするためにしていた工夫に気づくことができました。工夫のポイントは次の4点です。
- 項目名を設定対象にする。enable/disableなどで修飾せず、enable/disableを値とする。
- デフォルトの設定を、デフォルトの設定ファイル内にコメントとして入れておく。
- 同じ設定対象に複数の設定項目がある場合はグループ化する。
- enabled/disabledを設定項目にしたい場合はenabledのように肯定形を選ぶ。
設定ファイルを作るときは、すべて肯定形に統一しているかを気にしてみてください。設定の書きやすさ、理解のしやすさが変わります。
-
is_enabledやis_visibleなどの方が英語として自然ですが、そこまで変えるとここで説明しようとしている内容が増えて視点が分散してしまうのでis_enableでいきます。 ↩