まえがき
どうやったら品質の高い開発をチーム全体で行えるか考えていた時に、ベストプラクティスを広めるよりも、やってはいけないことを広めた方が効果が高いのではないかと思いました。
よくよく考えれば、ベストプラクティスなんかはよく調べているのに、やってはいけないことについてはちゃんと勉強したことがありません。
良い機会なので、そういったやってはいけないこととして広く認知されていることを調べて、他の人にも知ってもらえるように記事にしようと考えました。
この記事ではアンチパターンについてまとめています。
新人エンジニアの方は今後の業務に生かすために、経験者の方は共感を持って楽しんでいただければ幸いです。
アンチパターンとは何か
アンチパターンという用語は『オブジェクト指向における再利用のためのデザインパターン』という、開発におけるベストプラクティスをまとめた本をもじったものです。
「デザインパターンの反対の(anti)パターン」がアンチパターンです。
ただただ悪例の総称がアンチパターンと呼ばれたりしますが、実はちゃんとした定義があります。
- 動作やプロセス、構造についての繰り返されるパターンで、最初は有益だと思えるが、最終的に悪い結果をもたらすもので、
- リファクタリングするための方法が存在し、文書化され、実例で証明されており、再現可能であること
https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%81%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
最初から悪いと思われているものがアンチパターンであるわけではないのですね。
言うなれば、知らなければ自ら進んでいってしまう落とし穴のようなものがアンチパターンであり、存在や対処法を知っていればそれほど怖くないということです。
良いコードとは何か
実際のアンチパターンの紹介に進む前に、良いコーディングとはなんなのか、ここで確認しておきます。
良いコードとはつまり、保守性の高いコードです。
保守について
まだ現場に入ったことがない新人の方向けに保守について説明しておきます。
システムは開発して納品したら終わりではありません。
たまに成果物を他社さんに渡して、あとは向こうに任せるということもありますが、基本的にその後のメンテナンスは自分たち、あるいは保守チームの人が行います。
保守では「障害対応」「機能拡張」「セキュリティパッチの適用」「OSのアップデート対応」など、様々な対応をします。
「保守性が高い」とは
保守性の高いコードとはつまり、開発後のさまざまな対応において、開発にかかわらなかった人でも理解しやすく、追加機能などの拡張が容易であるということです。
もしもあなたがあるシステムの追加機能開発を頼まれた時、改修するコードが1千行近くある上に夥しい数のif文が何層にもネストしていたらどうでしょうか。
改修すべき箇所を特定するのにも時間がかかりますし、テストするためにどんな条件で行えばいいか理解するのにも追加の時間がかかります。
アンチパターンはその様な保守性の低いコードを意図せず生み出してしまうパターンのことです。
有名なアンチパターン紹介
アンチパターンとはなんなのかをみてきたところで、ここからは一般に知られているアンチパターンを見ていきましょう。
実際に自分や周りがやってしまっていないか考えてみてください。
スパゲッティーコード(Spaghetti Code)
アンチパターンという文脈以外でも耳にすることが多いかと思います。
理解しにくいコード全般を指す場合にも使われますが、意味的には処理やモジュール間の関係が複雑に絡み合っており、実行順序が把握しにくいコードのことを指します。
例を挙げると:
- グローバル変数があちこちで読み書きされている
- モジュール同士が連鎖的に互いを呼び出し合っている
- クラスで過度な継承を行なっている
- 整理されていない並列処理
などがあります。
中でも最も多いのはグローバル変数の多用と脈絡のないモジュール間の呼び出しです。
プログラミングを始めたての初学者がやりがちなパターンですので、初学者の方は自分のコードが分かりにくいものになっていないか、常に意識するようにしましょう。
打ち出の小槌(Golden Hammer)
別名銀の弾丸です。
ある場面でうまくいった方法が、別の場面でもうまくいくと信じ込むことを指します。
デザインパターンを学び始めた頃、自分もこれに陥っていました。
オブジェクト生成が必要になるたびにFactoryパターンを使ったり、開発の初期段階からAdapterパターンを取り入れたりしていました。
解決方法は本来、問題が起こった環境や状況に結びついているものです。
それがわからずに、自分が学んだばかりの方法をまるで銀の弾丸であるかのように扱ってしまうと、無駄な処理が増えたり、できたはずの最適化ができなくなったりしてしまいます。
何かのツールやプラクティスについて学ぶときは、それがなんのためのものなのか、どういう場合で効果的なのか考えつつ学びましょう。
ボートの碇(Boat Anchor)
役に立たないものを「いつか使えるかもしれないから」とそのままにしておくことです。
ホルダー気質のプログラマーバージョンといったところでしょうか。
そういう処理は大概、後になっても使いません。
システムは時間によって変化していき、求められるものも変わっていきます。
つまり、時間が経つほどにそのいつか使う処理を実装した時から状況が変わってしまいます。
どうしても残しておきたいなら、自分のPCのフォルダの中にひっそりとしまっておきましょう。
死んだコード
ちなみに、似た様なアンチパターンに 死んだコード(Dead Code) というものがありますが、そちらは「使わないとわかっているのに残されているコード」のことです。
経緯を残すために残されていることが多いのですが、そんなものはドキュメントに残すべきです。
今現在のシステムと関係のない処理はいたずらに混乱を招くだけでメリットはありません。
使わないとわかったらその場で消す様にしましょう。
神クラス(God Object)
処理を詰め込みすぎていて、責任範囲が過剰に広くなってしまっているクラス(オブジェクト) のことです。
「こいつさえ呼び出せば大丈夫」と同僚が実装したクラスを鼻高々に勧めてきたらその鼻を即座にへし折りましょう。(本当に実行しないように)
処理が一極集中していれば理解も容易いと考えるかもしれませんが、異なるビジネスロジックが混在しているクラスは変更が容易ではなくなります。
あと単純に1クラスに数千行も詰め込まれたコードはめちゃくちゃ読みにくい...。
ひとつのクラスはひとつのことに専念させる様にすべきです。
考えられる対策
- SOLIDに則ったクラス設計を行う
- クラスの処理がそのクラス名から推測できるものになっているか考える
マジックナンバー(Magic Number)
それがなんなのかの説明もなしに使われている数値のことをマジックナンバーと呼びます。
円周率(3.14
)などのよく知られている定数ならともかく、なんの脈絡もなしに現れた数値はコードを読む人にとっては謎の数字です。
いじってもいい数値なのか、もしいじったらどんな影響が出るのか、何も伝えて来ず、把握するために前後の処理を読み解く必要が出てきます。
決まった数値を使う際は必ず変数か定数に持つ様にしましょう。
円周率やデータ型の最大値などの数値はどの言語でも定数として用意されている場合が多いので、積極的にそれらの定数を活用しましょう。
カーゴ・カルト・プログラミング(Cargo Cult Programming)
これはコーディングスタイルやパターンを、その背景や目的を理解しないまま実装することを指します。
ドメイン駆動設計やクリーンアーキテクチャなどの設計パターンを学んだばかりの技術者が、よく理解もせず、ただ有用だと言われているからという理由で実装に使っている場合はこれに当てはまります。
どんな設計パターンやデザインパターンにも、それが考案された背景があり、利用する理由があります。
それを理解せずに場違いな場面で実践してしまえば、意図がよく理解できないコードやモジュールが出来上がってしまいます。
コーディングする際は、成果物についてちゃんとその様に実装した理由を説明できるかどうか、適宜振り返ることを心がけましょう。
コピーアンドペースト
全てのコピペが悪いとは思いませんが、その内実を理解しないまま実装に組み込む場合はカーゴ・カルト・プログラミングとなります。
あとがき
今回はアンチパターンについて紹介しました。
「やってはいけないよ」ということを、連想しやすい名前と共にまとめてくれているのはありがたいですね。
改めて調べてみると、今回紹介したもの以外にも知らなかったアンチパターンを知ることができてとても有意義でした。
ぜひ皆さんもご自分で調べてみて、今後のプログラマーライフの糧にしてください。