※これは気づいたら追加してくメモという名の愚痴です。ネタがあったらコメントや編集リクエストでよろしくー...と、書いて三年以上経ってから出くわしたうんコードも完全に網羅していてワロタ。
まえがき
Cの経験が長いプログラマは二つのタイプに分かれる。一つは独自にオブジェクト指向的に凝集度と結合度をコントロールする方法を考えるタイプ。もう一つはそんなことはお構いなしに組んでしまうタイプだ。
これは後者のタイプがC++でやらかしがちな例を列挙し、なぜそれをやらかすのかを推測し、それがなぜダメなのかを解説する試みである。私自身は前者なので後者の考えていることがよくわからないという動機もあるし。
ある意味アンチパターン集と言えるだろう。
力任せ
大体どの項目も同じことについて述べている気がするが、とりあえず「三日前の自分は他人」という言葉がある。それとノッている時に吐いたコードはほとんどの場合ク◯の山だ。まず間違いなく次の日あたりに後悔することになる。
- 聳え立つク◯の山
モジュールやメソッドやオブジェクトを巨大にしてしまうというのはよくある話だ。しかし、Cでも関数の分割は推奨されているのでこれはCの経験が長いからではなく そんなことはお構いなし だからこその現象だろう。
これ、お仕事ソースだとほとんどどこにでも存在する現象なので困る。 一つのファイルが一万行以上あるデバイスドライバのソース とか勘弁してほしいものだ。
- 乱◯パーティ
インターフェイスが定まっておらず、どこで何を呼んでいるのかわかったもんじゃない。その当然の帰結として凝集度が低く結合度が非常に高い。
症状は主に、似たようなプログラムを組むのに毎度毎度組み直すとかいう再利用性の低さとして現れる。インターフェイスは分割の要なので当然の帰結といえよう。こういうのに手を入れるのはおっかないんだよなー。
クラス、オブジェクトに関するもの
力任せよりは色々意識してる分ずっとマシなんだけど、なぜかこの手の間違いは後を絶たない。世界の謎。もうちょっと俯瞰で考えようぜ?
- オブジェクトになり得ないクラス
ソースを斜め読みしていてコントローラーとかプレイヤーとかマネージャーみたいなerやorな単語を見かけたら要注意だ。これらは オブジェクトになり得ないもののクラス である可能性が非常に高い。
コントローラ とか プレイヤー は データとコードの一体化ができていないからこそ存在する んだ。それらはデータやデータのように見えるものを持っていないのでオブジェクトにはなり得ない。勉強してるけどわかってない人はこれをシングルトンのように実装したりするんだけど...いやいやそうじゃないんだってば。
ではどうなっているべきなのか。ってのは それらによって操作される対象 こそがオブジェクトなんだ。反射的にわかりそうな程度の話なんだけど。だから修正するにはコントローラーとコントロールされるものを合体すればおk。このアンチパターンにはまってるってことはたぶんこの時点では「コントロールされるもの」はクラスとしては存在していないと思うけど。
で、正しくオブジェクトになっていればデータやコードがほぼ適切に分割されて自然に結合が最小限になる。オブジェクト指向はもともとそういうもの。間違っていると感じたら力技で押し通そうとしちゃダメ。
- 露出狂
全部public からアクセサの羅列まで、カプセル化不足なのがこれ...なんだけど、 全部public はともかくこれにも 間違った分割 という別の原因がある。上の オブジェクトでないものをオブジェクトにする というのもそれなんだけど、その結果でありがちなのがこれ。
さらに症状は 乱◯パーティ として連鎖していくことが多そうだ。組んでいてわかる兆候は カプセル化で悩んだ挙句やたらfriendにしたくなる あたり。そんな時はどちらかをもう一方のサブクラスにすべきものだったりする。「参照関係は可能な限り一方通行にしとく」というのを心がけると間違いを検出できるだろう。できないと思ったら何かが間違っているということだ。
- オブジェクトへの参照でないものをオブジェクトのハンドルにする
インスタンスのポインタや参照ではなく、整数の何とかのIDみたいなものをオブジェクトのハンドルにして、あとでswitch~caseで場合分けするというのがこのアンチパターンの定番だろう。
酷いのになるとRTTIよろしく多態でIDを返させて分岐したりする。その分岐後の処理を仮想関数にしてその分岐自体を多態でやればいいのに。