見出し画像

「Clean Code」は全プログラマーがバイブルにすべき名著だった

プロのプログラマとして働く方に、是非とも呼んでほしいと思ったのがこの本だ。
自分はこの本に出会い、コードを書くということへの向き合い方が大きく変化した。
今年読んだ本の中で、一番仕事に活かせた本と言い切ることが出来る。

もしあなたがプログラマであり、よりよいプログラマを目指しているなら、この本を読む理由はそれだけで十分と思う。

似た本で「リーダブルコード」という本もある。こちらの方が有名かも知れないが、個人的にはCleanCodeの方が読みやすく、より実務に活用出来たと思っている。


何故コードは美しくしなければならないのか

コードは動けばいいじゃないか、とまではいかなくとも、納期の短いプロジェクトでは時間をかけてコードをより美しくすること、ましてやコードを美しくする為だけに、動いているプログラムにわざわざ手をいれることは嫌煙されがちだ。

しかし、本書によるとクリーンでないコード=粗悪なコードは、開発の生産性をどんどん低下させていき、いずれ0にしてしまうとある。
将来に渡る生産性を低下させないためにも、コードは常にきれいな状態に保つ必要がある。

クリーンなコードは、つまり読みやすく、理解しやすく、テストがしやすいものだ。
これは長期的なコストが一番安くなることに繋がる。

当然ながら、コードは書かれるよりも読まれることの方がずっと多い(10:1以上だ)
であれば、今多少書くのが大変でも、将来の開発者(または自分)が、より読みやすいコードを維持しておくことが、重要であるといえる。

ボーイスカウトルール

現場で意見が分かれるのがこれだ。
ボーイスカウトにある規則
「キャンプ場を、自分が来た時よりもきれいな状態にすること」
これをソフトウェア開発に置き換えて
コードに手を入れる前よりも、周辺を綺麗にして(つまりリファクタリングして)コミットする。

これは、日本の開発現場に長くはびこる
「動いているプログラムは触るな」と真っ向からぶつかる考え方だ。
もし現場がこちらの考え方寄りであるなら、後述する単体テストを整備して、リファクタリングが可能な土壌を用意していく必要がある。

この件は別記事でまとめているので、こちらもまた読んでみて欲しい。

命名

この本に限らず、より読みやすいプログラム、保守性の高いコードについて書かれている本では、必ず一丁目一番地に出てくるのがこの「命名」だ。

変数や関数、引数、クラスやパッケージ、ファイルやディレクトリに付ける"名前"について。それらの名前の付け方が、粗悪なコードかクリーンなコードかを大きく分ける。
この「命名」は、ソフトウェア開発の基本ではあるが、つい疎かになりがちだ。

変数にしろ、関数名にしろ、名前をつけるうえで重要なポイントを本章からまとめた。

  • 意図が明確な名前にする
    ⇒「なぜそれが存在するのか?何をするのか?どのように使用するのか?」が名前だけで伝わらなければならない。

  • ハンガリアン記法やメンバープレフィックスは不要
    ⇒型情報の明示化や、クラスのメンバであることを明示化する目的で付けられるこれらの情報はノイズになる。型が変わった際にリネームの必要が出てくるという問題もある。
    コードの読み手に、変数や関数の名前の意味にだけ集中させるということで、これらを変数名に付けることは推奨しないとある。
    (残念ながら、私の現場ではどうしてもハンガリアン記法が染みついており、やめるに至っていない)

  • クラス名には、Customer、WikiPage、Accountなどの名詞、または名詞句を付けるべき。
    ⇒クラスは動作ではなく概念であるべきなので、動詞をつけるべきではない。また名詞であってもManager、Processorなどは広義過ぎて、その責任範囲が名前からは分からないのでNG
    DataやInfoもNG。構造体と言っているのと同じでクラスにつける名前としては不適切

  • メソッド名には、postPayment、deletePage、saveなどの動詞、または動詞句を付けるべき。
    ⇒メソッドはオブジェクトに命令する行為なので動詞であるべき。そうでないと何が起こるかが不明確になる。
    このメソッドを呼ぶと何が起こるのかが明確な名前であるべき。

  • 名前が長くなることを恐れる必要はない。内容をよく表す長い名前は、不可解な短い名前より優れている

関数

関数を書くうえで、とにもかくにもこれは頭に入れておく必要がある。

関数の第一規則は、小さくせよ。第二の規則は、さらに小さくせよ、です。
<中略>
関数の長さが20行に達することなど、ほとんどないようにすべきです。

Robert C.Martin; 花井 志生. Clean Code アジャイルソフトウェア達人の技 (アスキードワンゴ) (p.132-133)

これに加えて、もう一つ大事なことがある。

関数では1つのことを行うようにせよ。その一つのことをきちんとおこない、それ以外のことを行ってはならない

Robert C.Martin; 花井 志生. Clean Code アジャイルソフトウェア達人の技 (アスキードワンゴ) (p.135)

これらを破ると、関数はより複雑になり、保守性が大きく下がり、手がつけられなくなる。

引数

関数の引数については、どのようにするのがよいか。
本書では以下のように述べられている。

理想的には引数の数は0、その次が1~2、3は出来れば避けるべきで、4以上というのはよほどの理由がなければ避けるべき

引数が多いと、読み手は、これを目にするたびに解読が必要になる。
また引数が多いということは前述の単一責務の原則が守れていない可能性もある。
加えて、テスト観点からも引数が多いと大変になる。

フラグ引数は悪手

引数が増える要因として、"フラグ引数"がある。
関数の処理を分ける為に、modeFlagのような引数を追加して、その値に応じて動きを変えるという手法だ。
一見、最小限のコード変更で機能追加を果たせたように見えるが、これは悪手だとある。
理由として、引数が増えるデメリットに加え、この関数に2つ以上のことをさせることになる。まさに単一責務の原則を無視した状態になる。

staticについて

これはクラスメソッドに関する話になるが、
一般的には非staticメソッドを好むべきである。多能的な振る舞いが必要になることがあり得ないという場合のみ、staticとするべきである。

例外処理

リターンコードではなく、例外処理で異常を検知する

リターンコードやエラーフラグで異常を検知というのは、今でもよく使用されてしまう手法ではあるが、これは呼び出し側で、呼び出した後にすぐエラーチェックをしなければならならず、コードが雑然とする。さらにエラーチェックを忘れてしまう、エラーフラグなどで戻り値や引数が増えてしまうという問題もある。

throw newやtry, catchなどの例外処理を使用してエラーをさばくことが望ましい。

nullを返さない

nullチェックは、特にc++などではよく見かける。日常と言ってもよい。
しかし、これは問題のあるコードであると本書は述べている。

関数ではnullを返すべきではない。これは、自分たちの仕事の中のやっかいごとを呼び出し元に押し付けているといえる。

ではどうしたらよいか?
例外を送出するか、スペシャルケースオブジェクトを返すようにすればよい。

nullを渡さない

nullを返すこと以上に、nullを関数に渡すことは良くないこととある。

nullが想定されていない関数であれば、NullPointerExceptionが送出されるか、最悪落ちる。

関数内でnullをさばく処理を用意することを強制することになる。
しかし、多くのプログラミング言語において、呼び出し元からnullが渡された場合、これをうまく対処する方法はない。

理にかなった方法として、nullを渡すことは禁止とする。こうすることで、nullが引数に渡されたことは何かの間違いであるということに出来るからだ。

コメント

第一に、ダメなコードをコメントで取り繕ってはならない。
まずは適切な命名を関数や変数に行うようにし、コードの意図を伝えるようにする。そのうえでそれらを補足するような適切なコメントを書くべきである。

では適切なコメントとは何か?
本書では、それを
コードでうまく表現することに失敗したとき、それを補うのに使うものだとある。
あくまでもコードで表現しきれないことをコメントとして書き残すというスタンスが望ましい。

コードは変更され進化していくなかで、コメントは置き去りになる。いずれコードの動きとは異なった、嘘を伝えるものとなる。
嘘を伝えるコメントなど、存在しないほうが100万倍マシだ。

コメントは常に、それを最小限とするように努力すべきである。

コメントアンチパターン

  • コードと同じことを自然言語で書く
    ⇒コード自体を上回る情報を全く提供しないコメント。これらはノイズにしかならない。

  • 誤解を招きかねないコメント
    ⇒正確ではないコメント、あるいは将来的に正確でなくなる可能性があるコメント、現時点で古くなったコメント、不適切なコメント、正しくないコメントはただちに最新にするか取り除くべき。

  • すべての関数にJavadocの記述を強制する
    ⇒情報量ゼロの関数ヘッダコメントや変数コメントが量産され、コードが見ずらくなる。
    javadoc自体が悪ではない。"必ず書かなければならない"と厳密にルール化することが悪。

  • コメントアウトされたコード
    ⇒これが残っている現場は多いのではないだろうか。
    本書ではこれを「コードのコメントアウトほどいやな慣習はあまりありません。絶対にやめましょう」と言い切っている。
    今が60年代なら、コードのコメントアウトにも意味はあった。しかし今はgitやsvnなどのバージョン管理システムがある。消してそれが問題になることはない。今すぐに削除すべきである。

単体テスト

ここでいう単体テストとは、デバッグコードをプロダクトコードに埋め込んで、カバレッジを通すだけのテストではない。もちろん人がデバッカを使って処理を一つ一つ目で見ていくテストのことでもない。
ここでいう単体テストとは、xUnit系のテストコードが書かれた、1/100s以下の時間で自動で実行できるテストのことだ。

ここをまず大前提として知っておく必要がある。
ここが曖昧の場合は以下記事からまずは見ておいてほしい

汚いテストコードを持つということは、テストコードを持たないのと同値

まずは単体テストがプロダクトコードに整備されているという前提にはなるが、単体テストがあればOKという訳ではない。
持たないより悪い、ということには流石にならないが、テストコードが汚く、メンテナンスに多くの時間が割かれるようでは、結局保守性が下がり、メンテナンスコストが増加してしまう。

しかし、テストコードがないと、コードの変更が問題ないかを確認することが簡単には出来ない。
これが進むと前述した「ボーイスカウトルール」的思想から「動いているプログラムを触るな」的思想にシフトしていってしまう。

テストが汚いとコード変更の妨げになる。
汚いテストコードを許容せず、テストコードをも綺麗に保つということがまずは重要になる。

単体テストはCleanCodeの根幹になる

単体テストは、コードの
・柔軟性
・保守容易性
・再現性
を維持、提供する。

まさにClean Codeの根幹である。
理由は、単体テストがあれば変更を恐れずに行うことができるからである。そしてテスト網羅性が高いほどに、テストの信頼度が高いほどに、これらの改善が臆せず行えるようになる。

クリーンテスト

クリーンテストの要素について、本書ではこのように述べられている。

何が洗練されたテストを作り上げるのでしょう? 3つの要素があります。読みやすさ、読みやすさ、そして読みやすさです。テストコードにおける読みやすさは、製品コードにおけるそれよりもさらに重要といえるかもしれません。
何がテストを読みやすくするのでしょう? どんなコードでも同じです。明瞭さ、単純さ、そして表現の密度です。テストにおいては、多くのことを可能な限り少ない表現で言い表す必要があります。

Robert C.Martin; 花井 志生. Clean Code アジャイルソフトウェア達人の技 (アスキードワンゴ) (p.351).

もちろんテストコードを読みやすくする具体的なテクニックも存在する。
構築-操作-検査パターン(いわゆるAAAパターン)。またプロダクトコードではご法度とされるような書き方も、テストコードでは許容するという二重規範の考え方も重要である。
これらを駆使し、とにかく読みやすいテストコードを書いていくことが、汚いテストコードからの脱却に繋がる。

クラス

クラスを書くうえで、とにもかくにもこれは頭に入れておく必要がある。

クラスの規則の筆頭は、小さくするということです。第二の規則は、さらに小さくするということです。
<中略>
関数の場合と同様に、まず最初の課題は「どうやって小さくするか?」です。
関数のときは、物理的な行数を測定しました。クラスの場合にはこれとは違う方法で測定します。それは責務の数です。

Robert C.Martin; 花井 志生. Clean Code アジャイルソフトウェア達人の技 (アスキードワンゴ) (p.376)

関数で書かれていたことと、まぁ同じである。

単一責務の原則(SRP)

クラスは、ただ一つの責務を持つべきである。
これは、「変更の原因となるものはただ一つだけ持つべき」と言い換えられる。
変更の理由が複数あるクラスは、すなわち単一責務の原則に違反していることになる。

なぜこれが重要なのか?

変更理由が複数あるクラスは、変更の衝突が起こる。
変更理由AとBが同じクラスに混ざることで、Aを直すだけのつもりがBの機能を壊すということに繋がりかねない。

さらに前述した単体テストが難しくなったり、このクラスを呼び出すクライアント側が影響範囲を気を付けなければならなくなるなど問題も多い。

単一責務の原則は、オブジェクト指向設計の概念として最も重要なもののひとつである。
小さなクラスが溢れかえるのは、全体が見えにくくなるという懸念について、本書ではこのように書かれている。

小さなクラスの集まりとして構成されたシステムというのは、大きな少数のクラスで構成されたシステムよりも可動部分が少ないのです。大きな少数のクラスで構成されたシステムと、覚えなければならないものの数は変わらないのです。
<中略>
 大きなシステムというのは、大量のロジックと複雑さとを持っています。こうした複雑さに対処するのに必要なのは、開発者がいつでも直接理解しなければならない箇所を容易に探し出せるように、全体を構成するということです。
<中略>
 もう一度、前者のポイントを強調しておきましょう。システムは、少数の大きなクラスではなく、多数の小さなクラスで構成する必要があります。

Robert C.Martin; 花井 志生. Clean Code アジャイルソフトウェア達人の技 (アスキードワンゴ) (pp.384-385)

このクラスと単一責務の原則についてはこちらの記事で深掘りしている。

重複

本書の中でも特に重要なルールとされているのがこの重複
重複コードが存在するところでは、抽象化の機会が失われている。重複部分をサブルーチンや他のクラスにすることで、自身の設計の中で語彙を増やすことが出来る。別の開発者がこの抽象化機能を使用することが出来る。抽象化レベルをあげることで、コーディングを素早く行うことが出来るようになり、不具合も減る。

明らかにコピペを繰り返したようなケースは論外として、
同じ条件を扱うif/elseの連鎖。これは多能におきかえることが出来る。
さらに微妙な例として、ソースコードの行は異なるものの、似たようなアルゴリズムを持ったモジュールが挙げられる。これらはTemplateメソッドやStrategyパターンによって対処すべきである。

継承クラスに依存しない

ベースクラスは、継承クラスの知識を持つべきではない。もしベースクラスが継承クラスの名前やメンバを知っていたら、知っていないといけないつくりになっているとしたら、それは設計になんらかの問題があると思った方がよい。

まとめ CleanCodeを心がけるうえで重要なのは?

結局のところ、CleanCodeを心がけるうえで重要なことは、以下の4点と言えるのではないかと、本書を読んで強く感じた。

  1. 意味が伝わる適切な命名

  2. 小さく、単一責務となっている関数

  3. 小さく、単一責務となっているクラス

  4. 適切かつ最低限のコメント

いずれも、やること自体はそれほど難しいことではない。が、これらが徹底されているコードとそうでないコードとでは、その質はまるで異なってくる。

これらが満たせたうえで、単体テストを整備し、リファクタリングを行える土壌を構築していくことが、CleanCodeに繋がる。

最後に、CleanCodeを構築していく為の筆者のコメントを本書から引用して、この記事を締めくくりたい。

ご安心ください。筆者はいきなりこのようなプログラムを書き上げたわけではありません。さらに重要なことは、筆者は、皆さんにきれいで優雅なプログラムを一本道で書けるようになって欲しいわけではないのです。もしも筆者らがここ数十年の間に何かを学んだのだとしたら、プログラミングとは、科学というよりも工芸であるという点です。クリーンコードを書くためには、まず汚ないコードで始め、それをきれいにしていくべきです。
 これは驚くに値しないはずです。小学校でこの真実を学んでいるからです。先生は生徒に作文の下書きを書くように指導します(大抵は徒労に終わりますが)。先生が我々に教えてくれたやり方というのは、まず大雑把に下書きを書き、下書きの第二版を書き、さらに何度か下書きを重ね、最後に最終版を得るというものでした。先生が我々に教えてくれようとした、きれいな作文を書くという方法は継続的改良だったのです。

Robert C.Martin; 花井 志生. Clean Code アジャイルソフトウェア達人の技 (アスキードワンゴ) (pp.527-528).



いいなと思ったら応援しよう!

この記事が参加している募集