はじめに
この記事は、(実際に動いている)スパゲッティコードを見てきた私自身のふりかえりです。
- なぜ「スパゲッティ」になるのか?
- そうならないためにはどうすればよいのか?
私なりのベストプラクティスを集めたものです。
熟練者の方からすれば「あたりまえ」なこと、異論などあると思いますが、少なくとも、私はこれらを意識することで(多分)そこそこなコードが書けているので、投稿することにしました。
前提として、私のバックグラウンドはC#とRubyとTypescriptです。
そのため、これらの言語に寄っているかもしれません。
その辺りはご容赦ください。m(_ _)m
一番大事なこと
他の人が読んで、コードを作った人の意図がわかること。
「スパゲッティコード」と呼ばれるコードは、確かに論理的整合性が取れていなくて、見辛いということもあるのですが、本質的には「何書いてるのかわからない」「何をしたいのかわからない」からそう呼ばれるのだと思います。
そのため、「スパゲッティコードでない」と言うために一番大事なことは、
書いた人がきちんと意図を読み手に伝えるコードを書いているのか?
ということです。
それでは、読み手に意図を伝えるためのエッセンスを見ていくことにします。
目次
- 基本はSimpleであること(KISSの法則)
- DRYであることの重要性
- if ,while, for は本当に必要か?
- クラス、コンポーネント間の距離
- 名は体を表す
- オブジェクト指向原則って知ってますか?
- テスト容易性はきれいなコードにつながっている
- デザインパターンシンドローム
- ログは安くすます
Simpleであること
最も難しいことはプログラムを「単純(Simple)」に保つことです。
... なぜ難しいか?
その理由は、「単純」の定義自体が難しいからです。
「プログラムを単純に保て!」と言われても、作るべき要件や機能が増えるほど、その難度は増します。
:
この一つの答えは、設計指針を決めることです。
「こんな方針で設計するんだ!」をまず最初に決めることです。
なぜなら、スパゲッティコードの一番の理由は、「何書いてるかわからない」ことだからです。
設計指針があれば、第三者が見た時に「設計指針に従っているか?」が判断材料になります。
設計指針がプログラム全体から見えることがすなわち「単純」であることです。
参考
KISSの法則(単純なんだよ!バカっ!)
DRYであることの重要性
いわずもがな。
冗長なコードは止めましょう。
保守開発の現場では、どうしても「既存のコードは触りたくない」という気持ちもわかります。
ただ、もしその保守開発が長く続くのであれば、既存のコードを触ることも検討したほうがよいです。
余談ですが、私が経験したプロジェクトはこんなスパイラルに陥っていました。
冗長なコードを書く=影響範囲が見にくくなる=確認に時間がかかる=生産性が低下する=冗長な...
if, whileは本当に必要か?
プログラムの基本構造は 「順次」、「分岐」、「反復」です。
今も昔も変わっていません。
スパゲッティコードとして良く見られるのは、「巨大なクラス」「複雑な条件分岐」、「無駄に多い反復」です。
こうなる理由は、コーディングのバリエーションを知らないことにあります。
オブジェクト指向言語のいいところは、クラス構造をうまく考えることによって、
構造面から単純な解決策を模索できるところです。
複雑な問題も、順次構造とクラスの組み合わせで解決させられないかを模索することが、見やすいコードを書くためのポイントです。
クラスの距離、離れ過ぎていませんか?
「コンポーネントの間をできるだけ疎結合にしたい」
プログラマーなら誰しも意識することでしょう。
そのために多くの抽象クラスを作ったり、クラス間をバイパスする中間クラスを作ったりすることがあります。
確かに疎結合にはなるかもしれませんが、間にたくさんのクラスが入ることで、実際にやりたい処理をするクラスまでの導線が遠くなる、という一面もあります。そうなると犠牲になるのは可読性です。
抽象化を考える前に、「直結しても十分疎結合かどうか?」を 検討するほうが後々見やすくなります。
例えば、デザインパターン Abstract Factory と Factory どちらを選択しますか?
- 前者は抽象度が高いですがクラス間の距離は遠くなります。
- 後者は前者より抽象度は低くなりますが、クラス間の距離は近くなります。
もちろん適切なシーンにマッチしてると判断できるならそれを適用しますが、
迷うケースではFactoryの適用を優先して考えます。
名は体を表す
変数名を考えるときこそ集中しましょう。
ロジックを考えるのと同じくらい難しい作業ですが、根詰めてやりましょう。
文章を書くときの言葉選びと同じで、あいまい表現になっていないかを何回も確認すべきです。
余談ですが、
「temp や i や k などは使うな!」
私もよく言われましたが、これは本質ではありません。
ロジック上「あいまいさ」がなければ、temp や i や k だって使っていいんです。
以下の書籍がオススメです。
リーダブルコード
オブジェクト指向原則って知ってますか?
設計に迷ったとき、思いついた解決策に飛びつくことはよくあることです。
そのとき、目の前の問題だけに目が行き、全体のクラス構造から逸脱したことに気づかないことで、
スパゲッティへの火種ができてしまいます。
こんなときふりかえるべきは、デザインパターンではなく原則に立ち返ることです。
オブジェクト指向の法則集
これらの原則から逸脱するとき、明確な理由があるか。
逸脱するなら、説明が必要ということです。
「テスト容易性」はきれいなコードにつながっている
テストがしやすいということは、そのクラスが単純であるということができます。
グローバル変数やDBへの依存性が高いために、テストコードを書くのに壮大な準備作業が要るなら、
それは、クラス構造を見直したほうがよいです。
デザインパターンシンドローム
アンチパターンにはまると、可読性を損ないます。
でも「使わなきゃ」と考えている人は結構多いです。
デザインパターンには、柔軟性を得られる適切な「シーン」があり、デメリットもあることを理解してないことが原因です。
一番いい例がSingletonパターンの使い方。
オブジェクトをアプリケーション内で1つに留めることができますが、その実装はstaticなグローバル
オブジェクトとして実装されます。
安易に多用することは、グローバル変数を増やしているのと同じ行為であることを認識すべきです。
ログは安くすます
「ログを出すのに4行の行数が必要」、「コード上にログの組み立て箇所が数100箇所もある」
ログを出すだけでコードが長くなり、本当に表現したいロジックが埋もれてしまいます。
ログ出力はなるべく1行で書けるように工夫します。
これはログだけに限った話ではなく、大事なことは重要なロジックを表に出すように意識することです。
以上、私が設計、実装で意識している1+9 か条です。
必ずしもこれに従え、というわけではないですが、少しでも判断の助けになれば幸いです。