入力フォームや設定画面をつくるときのデータ構造アンチパターン

「なんで?」と聞かれるとうまく言語化できてない部分が多いけど、経験的に不吉な香りがするやつ。

デフォルトがtrueの選択肢

f:id:YusukeIwaki:20211112020238p:plain

デフォルトtrueの選択肢は、falseとNULLを厳密に区別しないといけなくなる。 item.value || true なんて書いた瞬間にバグる

  • ユーザがもともと全部チェックを明示的に外していた場合には、次回以降のフォーム描画は初期表示がunchecked
  • でも、 選択肢を1個増やすことになった場合には 、その増えた選択肢の項目は初期表示がchecked

falseとNULLを気をつけて区別したら、もちろん実装できなくはない。ただ、JavaScriptRubyなど、多くの言語ではfalseとNULLを区別しないほうが条件分岐が楽に書けるはずだ。

if item.value.nil?
  true
else
  item.value
end

こんな条件文、いちいち書きたい??俺はいやだ。

可能な限り、文言などで工夫して、デフォルトfalseで設計したい。

nullableなBoolean

これもnullとfalseの区別という観点だが、「ユーザが何も設定していない状態」がnullで、「ユーザが明示的に設定をオフにした状態」がfalse みたいになっていると、ややこしい。

たとえば(こんなシステムはないとは思うが)

  • ?in_stock=true で在庫がある商品を絞り込み
  • ?in_stock=false で在庫切れ商品を絞り込み
  • in_stockパラメータがない場合には絞り込まない

のようなシステム。in stock をチェックボックスで実装すると、在庫切れ商品で絞り込めない。

Booleanは確実にNonNullにしよう。

現在の状態を表すモデルと、更新フォームの値を保持するモデルが同じ

更新フォームと現在の状態を表すモデルは、Railsなどのフレームワークを使うと、同じモデルで済ませてしまうことが多い。

これだと、以下のように値を更新しない場合のバリデーションで分岐が必要になる。新規レコード作成時だけバリデーション、など。

ユーザー名
_Yusuke Iwaki___________

パスワード(変更しない場合には空白のままでOK)
________________________

あとは、RailsであればActiveRecord::Dirtyで多少の回避はできるが、ユーザが値を変更していないのに更新通知を出してしまうなどの事故も起きやすくなる。

更新フォームは、素直にフォームオブジェクトを使うようにしよう。

ほかにも...

他にも思いついたものはいろいろあった気がするが、いざ書いてみるとなかなか思い出せない。

思い出したらそのうち書く。