少し時間ができたので、これまで割とおろそかにしてきた基本的なことを学びたく、リーダブルコードを読んでみた。
そのメモ的なものを載せます。
第1章 理解しやすいコード
1.1「優れたコード」ってなに?
1.2読みやすさの基本定理
コードは理解しやすくなければならない。
理解しやすい=簡潔なこと?なのか?というと必ずしもそういうことではないらしい。
ということで、筆者の定義する読みやすさの基準としての考え方(基本定理)は、
コードは他の人が最短で理解できるように書かなければならない
ということ。詳細設計書が残ってても上記原則に則っていないコードをメンテナンスするのはほんとにきついですからね・・・
1.3小さなことは絶対にいいこと?
コードが短い=理解しやすい、と思ってたけど、読み手となったときに、省略されすぎててわからないことがあった。なので、コードが短いことが必ずしも理解しやすいとは限らない。
※もちろん短ければ短いほうが良いと思う。
1.4「理解するまでにかかる時間」は競合する?
コードを効率化するとか設計書を書くとかで何とかなるのでは?
ということと読みやすいコードを書くことは全くの別条件。
※経験談的にもそう。
もちろんその要素も重要だけど、その要素も実施したうえで理解しやすいコードを書くことにより、テスト設計も簡単になり、優れた設計にもつながる。
自問自答して、自分が読み手になったときにイラっとしないような、読みやすいコードを書くことが重要。
1.5でもやるんだよ
第2章名前に情報を詰め込む
変数であろうと関数であろうと名前を短いコメントだと思って情報を詰め込んでおくのが良い。
tmpとかは目的通り一時的なら問題ないけど、多用するものではない。
また、getとかset、sizeとかも一見具体的なようで抽象的らしい。
2.1明確な単語を選ぶ
getは抽象的ではない。
GetPageみたいな関数はそもそもどこから情報を取得するのか、webページから?データベースから?などが実はわかりづらい。
Downloadとかもっと具体的な言葉を使うべき。
※データベースからとってくるときはgetでいいのでは?と思うけど。。。
Sizeも高さとか長さとか幅とか別の表現が可能。
可能な限り別な表現ができるのであれば、より具体的な表現のほうが確かにわかりやすい。
カギとなる考えとしては
気取った表現より明確な表現
確かに明確で具体的なものが一番いいと思う。
2.2tmpやretvalのような汎用的な名前を避ける
やりがちだ。
retvalは「戻り値」という意味以外は持たない。
戻り値として使う分には問題ないけど、これを関数に関数の引数として使ったりするのは違う。
tmpも「一時的な保存」という使い方なら全く問題ない。
tmpという名前は、生存期間が短くて、一時的な保管が最も大切な変数にだけ使おう
ループイテレータ
i,j.k自体を使うことは問題ないが、わかりづらいことがある。
i,j,kすべてを使う(そんな場面はなかなかないけど・・・)ときはわかりづらくなることがある。
説明的な変数名にすると混乱しづらい。
XXXX_iとかXiとか。
ループの中にループ、みたいな場面でなければ特に気を使わなくてもよく、目的を間違わなければ汎用的な変数名でも全く問題ない。
2.3抽象的な名前よりも具体的な名前を使う
役割が判断しやすい関数名にしておかないと、使う側と使われる側で齟齬が生じる。
抽象的すぎると後で見返すときにどういう役割かがわからない場合がある。
中身と名前が一致するような名前にすること。
2.4名前に情報を追加する
名前は短いコメントようなもの。
変数名に込められる情報はあまり長くないが、絶対に知らせたい情報を単語として変数名に入れられればいい。
16進数の何かなら、hex_xxとすると、この変数は16進数の情報を必要としていることが一目でわかる。
また時間ならxx_msとかxx_secとか、始まりならstart_xx、終わりならend_xxとかも付けられる。
また、その他の重要なメッセージも込められる。
非暗号のパスワードなら、password_plainとか、utf-8の文字列の情報ならxxxx_utf8とか。
わざわざコメントで書かなくても変数でわかるので、いちいちコメントを見返すことも、勘違いのリスクも減ることになる。
2.5名前の長さを決める
長い名前を避けるという暗黙の了解がある。
が、短くしすぎて結局その変数の意味が分からなくなってしまうのは本末転倒である。
識別子のスコープが短い=コード行数が短い場合は名前は短くても、見えるところに変数定義があるから、短くても問題はない。
※が、どれぐらいこの場合があるのか・・・?
長い名前を入力すること自体は問題ではない。
実際、各エディタには単語補完の機能がある。
vi | emacs | Ecrips | IntelliJ IDEA | TextMate |
---|---|---|---|---|
Ctrl-p | Meta-/(ESCを押してから/) | Alt-/ | Alt-/ | ESC |
とはいえ、長すぎるとやはり見栄えはよくないし、わかりづらくなる。
そのため、単語を省略する。
stringならstrとか、numberならnumとか、documentならdocとか。
ただ、全員が共通でわかる略語でないと意味ない。
※会社名とか入れてるとよくない。確かに。
あと、省略できるケースとしては、省略しても情報が失われないケース。
convertToStrだったら、ToStrでも十分。
NumToStrだったら必要だと思う。結局は時と場合によってしまうということか。
2.6名前のフォーマットで情報を伝える
ローカルな関数なら_を使い、外部APIなら小文字と大文字を組み合わせるとか、そういうところでも情報を伝えられる。
コード規約によると思うので、開発プロジェクト内で分かりやすい表現方法で記入すること。
Googleの規約とかではかなり細かく決まっているみたいで、Class名とかClass内の変数とかわかりやすくなっているらしい。
※時間があるときに読むか。。。
とにもかくにもプロジェクト内で分かるようにした規約を作る必要がある。
2.7まとめ
- 明確な単語を選ぶ --例えば、Getではなく、状況に応じてFetchやDownloadなどを使う。
- tmpやretvalなどの汎用的な名前を避ける --ただし、明確な理由があれば話は別だ。
- 具体的な名前を使って、物事を詳細に説明する --SeverCanStart()よりもCanListenOnPort()のほうが明確だ
- 変数名に大切な情報を追加する --ミリ秒を表す変数名には、後ろに_msをつける。これからエスケープが必要な変数名には、前にraw_をつける
- スコープの大きな変数には長い名前を付ける --スコープが数画面に及ぶ変数に1~2文字の短い暗号めいた名前を付けてはいけない。短い名前はスコープが数行の変数につけるべきだ。
- 大文字やアンダースコアなどに意味を含める --例えば、クラスのメンバ変数にアンダースコアをつけて、ローカル変数と区別する。
第3章 誤解されない名前
カギとなる考え
名前が「他の意味と間違えられることはないだろうか?」何度も自問自答する。
3.1例:filter()
results=Database.all_objects.filter("year <= 2011")
上記の場合resultsに何が含まれているべきなのかがあいまいだ。
filterが曖昧で、除外したいのか選択したいのかが読み取れない。
(2011以下のオブジェクトを選択した結果が欲しいのか、2011以下ではない結果が欲しいのか。)
そのため、2011以下のオブジェクトを取り出したいのであればselect()とすべきだし、除外した結果が取り出したいのであればexclude()を使うべきである。
3.2例:Clip(text,length)
切り抜く関数
def Clip(text,length):
という名前は2通りの動作が考えられる。
・最大length文字まで切り詰める
・最後からlength文字を削除する
※これは受け取り方の問題??
確かに削除ならremoveのほうが直接的であるので、なるべく読み手に取って一つの意味にしかとらえられない単語を使うように意識すること。
また、length自体も色々読み取れるため、最大ならmax_を、最小ならmin_をつける、また文字列だったらmax_charsという書き方に書くほうがベターである。
3.3限界値を含めるときはminとmaxを使う
LIMITという表現は下限なのか上限なのかわからないということ。
確かに。
MAX、MINという表現を使うことを心がけること。
3.4範囲を指定するときはfirstとlastを使う
first、lastだとそれぞれ最初と最後が包含されていることがわかりやすい。
未満であれば<を使うとかで対応も可能である。
3.5包含排他的範囲にはbeginとendを使う
beginではなくstartでもいいような・・・
一般的にはbeginを使い、その対になるものとしてendを使うようだ。
3.6ブール値の名前
trueとfalseを明確になる名前にすべき。
is_XXXXとか、has_XXXXとか。
あと、否定形ではなく、肯定形をつかう。
disable_sslではなく、use_sslなど。
3.7ユーザーの期待に合わせる
get_xxという名前だと、簡単に何かしらの値を取得することを期待される。
が、実際に関数内のロジックで複雑なことをやっていると、get_XXXという名前なのに、思ったよりコストがかかるじゃないか!となるので、時間がかかる処理の場合はcomputとか別の関数名にすべきである。
size()の計算量がo(n)であった場合にも気軽に呼び出したが故に、思ったより時間がかかってしまうように思えてしまう。
(計算量の話はここを参考->https://qiita.com/asksaito/items/59e0d48408f1eab081b5)
C++標準ライブラリではsizeという名前の場合は計算量はO(1)であることを定められている。
そのため、countを頭につけるなど、名前を変える。
※ここはC++標準の話かもしれない?ので要調査!!!!!
3.8例:複数の名前を検討する
複数の名前を候補に挙げ、それぞれの長所を考える必要がある。
それぞれの名前が誤解を与えないか、別の意味を持っていないか、などの観点で検討する必要がある。
3.9まとめ
最善の名前=誤解されない名前であり、相手に自分の意図が伝わる名前であることが重要である。
第4章 美しさ
段落や横幅など、優れたソースコードは目に優しいものでなくてはいけない。
3つの原則として
- 読み手が慣れているパターンと同じレイアウトを使う。
- 似ているコードは似ているように見せる。
- 関連するコードをまとめてブロックにする。
4.1 なぜ美しさが大切なのか?
プログラマにとってプログラミンをする時間のほとんどはプログラムを読んでいる時間であるが故、読む時間が短ければ短いほうがよいためだ。
4.2 一貫性のある簡潔な改行位置
コードの見た目を一貫性のあるものにするには、適切な改行をいれ、また、コメントも整列させよう。
4.3 メソッドを使った配列
重複している処理をだらだら書いていると美しくない。
そのため、重複している処理を抜き出して、その部分をメソッド(関数)化することによって見た目が美しくなる。
副作用として、
- 重複を排除したことでコードが簡潔になった。
- テストケースの大切な部分が見えやすくなった。
- テストの追加が簡単になった。
という効果が得られる。
コードの「見た目」を良くすればコードの構造も改善できる。
4.4 縦の線をまっすぐにする
同じ関数を並べる時でも、引数の位置を同じ列にそろえておくと、それぞれどの引数に何を渡しているかが一目瞭然である。
配列でも同様。スペースやタブでそろえておくと後で見やすい。
4.5 一貫性と意味のある並び
コードの並びがコードの正しさに影響を及ぼすことは少ない。
ただ、ランダムに並んでいると、後で見てわかりにくい。
- 対応するHTMLフォームのフィールドと同じ並び順にする。
- 「最重要」なものから重要度順に並べる。
- アルファベット順に並べる。
※どの並び順にしても、一連のコードの中では同じ並び順を使うこと!
4.6 宣言をブロックにまとめる
変数宣言は同じ種類のものをまとめたほうが後で見やすい。
そうしないと、変数が足りてるのか足りていないのかが後で見てわかりづらい。
4.7 コードを「段落」に分割する
文章は複数の段落に分割されている。以下の理由からである。
- 似ている考えをグループにまとめて、他の考えと分ける。
- 視覚的な「踏み石」を提供できる。
- 段落単位で移動ができる。
コードも同じ理由で段落に分割すべきだ。
4.8 個人的な好みと一貫性
一連のコードの中では一貫性が大事だ。コード規約に従うのもそのためである。
一貫性のあるスタイルは「正しい」スタイルよりも大切だ。
4.9 まとめ
- 複数のコードブロックで同じようなことをしていたら、シルエットも同じようなものにする。
- コードの「列」を整理すれば、概要が把握しやすくなる。
- ある場所でA・B・Cのように並んでいたものを別の場所でB・C・Aのように並べてはいけない。意味のある順番を選んで、常にその順番を守る。
- 空行を使って大きなブロックを論理的な「段落」に分ける。
第5章 コメントすべきことを知る
コメントの目的は、書き手の意図を読み手に知らせることである。
他の誰かがコードを見るとき、書き手が考えていたことを理解するにはコードしか情報がないわけだが、それを補足するのがコメントである。
5.1 コメントするべきでは「ない」こと
コードからすぐにわかることをコメントに書かない
新しい情報を与えるでも、読み手がコードを理解しやすくなるわけでもないコメントには全く価値がない。
が、コメントをつけることにより読み手の理解が早まるのであれば少し価値はありそうだ。
以下はコメントすべきかどうかの判断をするポイントだ。
コメントのためのコメントをしない
これはコメントすることを目的とするな、という意味っぽい。
ひどい名前はコメントをつけずに名前を変える
優れたコード>ひどいコード+優れたコメント、と言われていることからも、まずはコードを改善できないかを検討すべきだろう。
5.2 自分の考えを記録する
では、何をコメントすべきか。
それは自分がコードを書いているときに考えている「大切な考え」だ。
「監督のコメンタリー」を入れる
・なぜ、この関数を使っているのか?
・バグっぽいのが残ってしまっている理由は?
・コードの汚さの理由は?
らへんを書いておくと、引き継いだ人はなぜ、このコードになっているか、ということがわかる。
そうすることで、直すべきなのか、直さないべきなのか?ほかに影響はないのか?等々が判断しやすい。
コードの欠陥にコメントをつける
よく使う記法として、代表的なものに以下のものがある。
記法 | 典型的な意味 |
---|---|
TODO: | あとで手を付ける |
FIXME: | 既知の不具合のあるコード |
HACK: | あまりキレイじゃない解決策 |
XXX: | 危険!大きな問題がある |
もちろんプロジェクトのコード規約によって使い方は変わる。
定数にコメントをつける
定数を定義するときに、その定位数が何をするのか、なぜその値なのか、という背景を持っている。
何か背景があるときは書いておいたほうが後で見返したときに変える、変えないの判断もつきやすいだろう。
5.3 読み手の立場になって考える
質問されそうなことを想像する
自分がコードを読んだときに「えっ、これって何なの?」と思うことを想像し、コメントをつける。
自分が疑問に思うということは他人も必ず疑問に思う。
ハマりそうな罠を告知する
このコードを見たときに他人がびっくりすることは何だろう?どんな風に間違える可能性ああるだろう?と自分に問いかける。
コードを使うときに直面する問題を前もって予測する。
「全体像」のコメント
新しいメンバにとって、最も難しいのは「全体像」の理解である。
システム全体が理解できないまま実装を進めてしまうと、システムにとって重大なバグを生み出しかねない。
ソースコードだけではわからない情報なので、短くても書いておくべきである。
要約コメント
関数自体が何をするものなのかをコメントするのもいい。
ただ、あくまで優れたコメントより優れたコードだ。
5.4 ライターズブロックを乗り越える
とにかくまずは思ったことを書いてみる。
やばいとか、このままではだめだとか。
書いてみた後に、もっと別の表現ができないか、と考え改善していく。
まずはとにかく伝えることが第一だ。
5.5 まとめ
コメントの目的とは、コードの意図を読み手に理解してもらうことである。
コメントすべきでは「ない」こと
- コードからすぐに抽出できること。
- ひどいコードを補う「補助的なコメント」。ただ、基本的にはコメントを書くのではなく、コードを修正する。
記録すべき自分の考え
- なぜコードがほかのやり方ではなくこうなっているのか
- コードの欠陥をTODO:やXXX:などの記法を使って示す。
- 定数の値にまつわる「背景」
読み手の立場になって考える
- コードを読んだ人が「えっ?」と思うところを予想してコメントをつける。
- 平均的な読み手が驚くような動作は文書化しておく。
- ファイルやクラスには「全体像」のコメントを書く。
- 読み手が細部にとらわれないように、コードブロックにコメントをつけて、概要をまとめる。
第6章 コメントは正確で簡潔に
コメントは領域に対する情報の比率が高くなければならない。
6.1 コメントを簡潔にしておく
すべて言語である必要はない。
->などを使えば言語で表現しなくとも伝わる。
6.2 あいまいな代名詞を避ける
その、あの、ではなくデータの、とか一番目のとかをつけること。
その、あの、では誤解を与えてしまうことがある。
6.3 歯切れの悪い文章を磨く
例:
これまでにクロールにしたURLかどうかによって優先度を変える。
ではなく
これまでクロールしたURLだったら優先度を高くする。
とするとより簡潔で直接的である。
6.4 関数の動作を正確に記述する
6.5 入出力のコーナーケースに実例を使う
慎重に選んだ入出力の実例をコメントに書いておけばそれは1000の言葉に等しい。
6.6 コードの意図を書く
コメントは自分の考えていたことを相手に伝えるためのものだ。
コードそのものの言葉で書くのではなく、抽象的な言葉にするべきだ。
listを逆順にいてレートする
という言葉ではなく、
値段の高い順に表示する
とすれば、作者の意図が伝わりやすく、その意図通りに動いているかどうかを確かめればよくなる。
6.7 「名前付き引数」コメント
インラインコメントで、関数呼び出しの際に使っている引数がそれぞれ何かをコメントすればよい。
6.8 情報密度の高い言葉を使う
コメントが長くて冗長だと読む気をなくしてしまう。
それほど長いわりに情報が少ないということだ。
長くなってしまうようならほかの表現で短くできないかを検討しよう。
6.9 まとめ
- 複数のものを指す可能性のある「それ」や「これ」などの代名詞を避ける
- 関数の動作はできるだけ正確に説明する
- コメントに含める入出力の実例を慎重に選ぶ
- コードの意図は、詳細レベルではなく、高レベルで記述する。
- よくわからない引数にはインラインコメントを使う(例:Function(/arg=/ ...))
- 多くの意味が詰め込まれた言葉や表現を使って、コメントを簡潔に保つ