ziguzagu.org

SQLアンチパターン:ネガティブワード・ポリグラフ(すべてにNoで答えなさい)

昨日職場でちらっと話していた、個人的に気をつけているというか回避しようとしてることに、SQLアンチパターンにのってそうな名前をつけてみた(文体もそれっぽくかいてみる)。

SQLに限った話でもなく純粋にクラス設計のアンチパターンともいえると思う。

目的:デフォルトで否定したい

デフォルトの挙動が否定側のような意味や、否定となるレコード数が支配的になることがわかっている場合そのままの意図をテーブル名やカラム名に持ち込みたくなる気持ちも理解できます。

アンチパターン:否定的な単語をつけtrueをデフォルト値としてしまう

create table awesome_functions (
  disabled bool not null default true
);

スキーマだけ見ると「ふむ」と一瞬納得しかけるが、コード上では結構えぐいことになっている or なっていくはず。

if !awesome_function.disabled?
  # 一部の人はイケてる機能を実行
end

あるいは以下。

unless awesome_function.disabled?
  # 一部の人はイケてる機能を実行
end

否定の否定は、何をしたいのかそれはどのような条件なのか書いた本人がその瞬間しかわかりません。もしくは本人もよくわかっていない可能性が高く、自分で嘘をついたのかどうかすら判別不能のはずです。

アンチパターンの見つけ方

否定形の接頭詞(dis-、non-、not-、un-、im- など)が現れた場合はすでに危険が及んでいます。 否定的な単語(failed、negative など)が現れた場合、ひょっとしたら何かを間違っている可能性があります

アンチパターンを用いても良い場合

それがユビキタス言語でありドメインを理解している関係者にとって自然な場合には用いても良いかもしれません。 (が、自分の経験上は思いつきませんでした…)

解決策:ポジティブに

カラム名の場合

ほとんどの場合ポジティブな単語に入れ替え値を反転させるだけで意図や条件はより伝わりやすくなります。

if awesome_function.enabled?
  # 一部の人はイケてる機能を実行
end

すでに存在してしまっているものについては反転した値を返す肯定形で別メソッドを使いそちらを標準的に使うことでコード上での問題はある程度回避できます。

def enabled=(val)
  disabled = !val
end

def enabled?
  !disabled?
end

テーブル名の場合

ORM の機能をつかってテーブル名に否定形単語がはいったまま肯定形単語のクラスとすることもできるかもしれませんし、チャンスがあればテーブル名を変えることも検討できるとよいですが、おそらくはそのタイミングでクラス名や場合によってはそのオブジェクトを保持している変数名まで変更せねばならず、影響範囲がかなり大きなリファクタリングを行うことになってしまいます。しかもロールバックがしんどい。

この場合は、過渡期を経て新しい肯定形の名を関したテーブルに移行する計画を立てましょう。それに価値が見合うのであれば…。

まとめ

うそ発見器にかけられたときのようにすべてを「No」で答える必要はありませんし、「No」が正解となる答える前提で質問を考える必要はありません。

前向きに人生をおくりましょう。