10秒ルールを深く考えよう
あまりにも、オブジェクト指向を嫌う人や、JavaやRubyを使ってもOOPにほど遠いプログラムが多すぎる。ので、またちょっと書いてみました。とにかく、他人に優しく。これがオブジェクト指向の基本中の基本です。
OOPってのは、会話するように(あるいは文章のように)プログラムを書くことだ、と、事ある毎に言い続けています。
つまらんナニガシパターンだの継承だのポリモル何とかなんていう知識を学ぶ前に、誰が読んでもわかりやすいプログラムを書く心がけ・気配りの方が、ずーっと大事。
極めてシンプルなんですよ、精神論だから。小難しいこと言うヤツの方がバカだと思え!・・・と、そのくらい強気で言えます。
OOPの目的ってのは、部品の再利用がもたらす工数削減です。
しかし、そこに捕われすぎると、誰も理解できない(つまり誰も再利用できない)難解なプログラムになってしまって、結果的に、そのプログラムを再利用しようとした後続プロジェクトでは、めちゃ大きな工数をかけてしまうことになるのです。
そんなことになるくらいなら、誰でも10秒で読めるプログラムを書いて、「直すならご自由にどうぞ」というスタンスをとるべき。だから、
「会話をするように、プログラムを書くこと」なのです。
10秒で読めるコードというのは、ひとりよがりでは書けません。
今日は、そういう話。
Userというモデルがあって、ADMIN権限をもつユーザーなら、他のユーザーを削除できるコントロールがあるとします。
(以降のサンプルではRails的に書いてますが、他の言語でも同じこと。)
User.destroy(params[:id])
end
↑これは論外。理由は直ぐにわかると思う。が、蛇足ながら説明する。
まず、「1」が何を示しているのかわからない。
ということは、設計書にがっつり説明文を書いて印刷してバインダーに閉じる。バインダーをめくって調べる。。。
という膨大でアナログな仕事が待っている。
Const::ADMIN_USER | Const::MANAGER_USER
:
end
↑これは、よく見る手法。定数のビット演算で権限を判定したりして。
でも、前述の問題をあまり解決していない。
書いた本人は「簡単だろ?」と豪語するかもしれないけど、ひとりよがりで、これも論外だ。このコードを引き継いだ人は、このコードの正しさを検証するために、結構な時間を使う。例えば、
比較演算子を間違えて代入(=)してしまうと必ずtrueになるから、
生半可動くコードができてしまう。
このコードをコピペした人は、バグを引きずる。
そもそも、auth_flgという曖昧な意味のプロパティをモデルの外部に見せてはいけない。なぜなら、auth_flgという単語で連想することが、あまりにも曖昧だから。ということは、
その設計書を読まなければならない。
バグに神経をすり減らす。
:
と、モデル以外の領域のプログラマにとって、余計なことを考える時間が増えるからです。
このコスト、計算してみてください。
100人のプロジェクトで、auth_flgってナンだ?どう使うんだ?と全員が思ったら、100時間のロス。きっと一度では覚えられないから、もう200時間をロスし、誤解やケアレスによる実装ミスに割く時間は×10。
カラム1個への気遣いがナイために、数千万円のロス(あるいはサービス残業)なんていう現場は、たくさんあると思います。ナイとは言わせないぞっ。
:
end
ですよね。設計時点における「ADMIN権限」という条件は脆いもので、
:
end
↑こんな風に条件が増えることも十分に考えられます。だから
:
end
かな、と思います。「消せるなら消す」という単純な文章になりました。
ここでもう一つ重要なのは、「コントロールはテストしづらい。モデルはテストしやすい。」という事実。そういう意味で、コントロールにはなるべくプログラムを書くべきではありません。モデルが持つべきロジックは、モデルに書いて、モデルでUnitテストをしたい。
ここからは、モデル~テーブルの設計の話。2例ほど。
■auth_flg
まず、命名の話。
プロパティやテーブルカラムに、xx_flagとか、xx_kubunとか入ってる場合、設計を疑いましょう。先に述べたとおり、曖昧すぎる命名だし、自然言語(話し言葉)ではナイからです。
■権限ビット
def admin?
Const::ADMIN_USER == self.auth_flg & 0xff0000
end
def manager?
Const::MANAGER_USER == self.auth_flg & 0x00ff00
end
end
こういうときにだけ、なぜか多用されるビット演算
いやいや、頭の良いフリしないでください。権限チェックだけ実行速度が良くても、なんの足しにもなりません。フレームワークが消費するCPUの方が、数100倍高いからです。
それよりも、このご自慢のハックが周囲にどれだけ迷惑をかけることになるか、気づくべきです。
素直に、↓これでいいんじゃないの?
:
ADMIN BOOL
MANAGER BOOL
:
)
class User extends ActiveRecord::Base
alias admin? admin
alias manager? manager
def can_delete_user
self.admin? || self.manager?
end
end
ダメだというなら、ダメな理由を教えて欲しい。だれも何も疑う必要のない、単純なコード。これこそがオブジェクト指向だと私は思います。
Railsの場合なら、テーブルカラムは値ではなくてメソッド、いうこと。
は、
ではなくて、
という発想。だったら、もうちょっといろんなメソッドをテーブルに投影することもできますよね。
:
ADMIN BOOL
MANAGER BOOL
:
DELETED_BY_ADMIN BOOL
LOCKED_BY_3TIMES_FAIL BOOL
:
)
if login_user.locked_by_3times_fail
:
end
(Javaだと、get/setが付いちゃうのが辛いところですが、考え方は同じ。)
DELETE_KUBUN、LOCK_FLG、なんていう命名よりは、はるかに生産性が高くなります。
オブジェクト指向の高い生産性とは、それを作ってる本人が得るものではありません。だから、テクニックを競う場でもナイのです。
いかに、周囲にポジティブな影響を与えていくか。他人に対するやさしい心配りでやさしい文章を書くようにプログラムを組むことが、
実はオブジェクト指向を極めていく大きな一歩なのではなかろうかと。
(完全なる自論)

