プログラミングの世界にも、共通する原則や法則があるのだそう。
今回はその中のいくつかをまとめた。また、『リーダブルコード――より良いコードを書くためのシンプルで実践的なテクニック』を読んで、よいコードとは何かを学んだのでまとめてみた。
ちなみに、 原則とは、多くの場合に共通に適用される基本的な決まり、法則のこと。
法則とは、守らなければならない決まり、規則のこと。
目次
- DRYとは
- KISSとは
- YAGNIとは
- SOLIDとは
- よいコードとはなにか
- 今後の課題
今回の学習のゴール
- プログラミングのマナーを学ぶ
- DRY, KISS, YAGNI, SOLIDについてわかる
- よいコードとはなにかを知る
DRYとは
Don't Repeat Yourselfを略したもの。
コードなどの情報が重複すれば変更が難しくなり、不一致が生じる可能性あるため、重複すべきではないとの規則。
KISSとは
Keep it simple, stupidを略したもの。
複雑でなく簡潔である設計は、成功への鍵だとの意味。
YAGNIとは
You ain't gonna need itを略したもの。ちなみに、ain'tはare notをの短縮したもの。
余計な機能があると、その分の時間が無駄になるため、機能は実際に必要となるまでは追加すべきでないとの規則。
SOLIDとは
オブジェクト指向プログラミングの原則(Principle)、5つの頭文字をとったもの。
- Single Responsibility Principle(SRP)
- 単一責務の原則
- 1つのクラスに複数の役割が存在してはいけないとの意味
- Open/closed principle(OCP)
- 開放閉鎖の原則
- 変更が発生した場合、既存のコードは修正せず、新しくコードを追加することで対応すべきとの意味
- 変更に耐えられる設計にしておくことが重要である
- Liskov substitution principle(LSP)
- リスコフの置換原則
- リスコフとは、アメリカの計算機科学者バーバラ・リスコフ氏を指す
- 派生クラス(スーパークラス、親クラス)は、基底クラス(サブクラス、子クラス)と置換可能でなければならないとの意味。
- Interface segregation principle(ISP)
- インターフェース分離の原則
- インターフェースはクライアント(上位レベルのモジュール)ごとに細かく作るべきとの意味
- クライアントが利用しないメソッドを持った太ったインターフェースは作ってはいけない
- Dependency inversion principle(DIP)
- 依存性逆転の原則
- 上位モジュールと下位モジュールは、抽象に依存すべきである
- 上位のモジュールが下位のモジュールに依存しすぎた場合、下位モジュールの変更が連鎖的に上位モジュールに影響を与えてしまうため、上位モジュールは下位モジュールに依存してはならない
※モジュールとはひとまとまりの機能・要素のこと。ソフトウェアでは、ある機能を実現するプログラムの塊を指す。ちなみに、ハードウェアでは、細かい部品を組み合わせて、ある機能を実現するひとまとまりの部品を指す。 おおもとである主モジュールは上位モジュールで、取り扱いやすいように分割されたモジュールは下位モジュールと呼ばれる。上位モジュールは、下位モジュールを呼び出してモジュールを使う。
よいコードとはなにか
1. 理解しやすいコード
- 読みやすさの基本定理
- コードは他の人が最短時間で理解できるようにかかなければいけない
- コードは短い方がいいが、それよりも理解するまでにかかる時間を短くする方が大切
- 理解するまでにかかる時間とは、変更を加えたりバグを見つけたりできるという意味
2. 名前に情報を詰め込む
- 変数や関数、クラスなどの名前は短いコメントととらえ、良い名前をつければ短くても多くの情報を伝えることができる
- 良い名前とは
- 明確な単語を使う
- 明確な理由がない限り、tmpやretval(return value)のような汎用的な名前を避ける
- 抽象的な名前でなく具体的な名前を使い、物事を詳細に説明する
- 名前の長さを決め、長い名前は避ける
- スコープの大きな変数(スコープが数画面に及ぶ変数)には長い名前をつける
- プログラマは頭文字や省略形を使って名前を短くすることがあるが、プロジェクト固有の省略形を用いることはNG
- 名前のフォーマットで情報を伝える
- アンダースコアやダッシュ、大文字を使って、名前に情報を詰め込むことが可能
- ex) クラス名はCamelCase(キャメルケース), 変数名はlower_separated(小文字をアンダースコアで区切ったもの)
- アンダースコアやダッシュ、大文字を使って、名前に情報を詰め込むことが可能
3. 誤解されない名前
- 最善の名前とは、誤解されない名前である
- 誤解されない名前を命名するために心がけること
- 命名した名前が別の意味としてとらえられないか何度も自問自答する
- 限界値を含めるときは、未満か以下なのか等まではっきりと理解できるように、minとmaxを使う
- ブール値(true, false)の変数やブール値を返す関数の名前は、頭にis, has, can, shouldなどをつけてわかりやすくし、否定形は使わないようにする
- 複数の名前を候補にあげ、それぞれの長所について考慮した上で、命名する
4. 美しさ
- 優れたソースコードは目に優しいものでなくてはならない
- コードの見た目を一貫性のあるものにするために、適切な改行を入れる
- 縦の線をまっすぐにし列を整列させることで、概要が把握しやすくなる
- 意味のある順番を選んで、常にその順番を守る
- ex) ある場所でA, B, Cと並んでいたものを、他の場所でB, C, Aのように並べてはいけない
- 複数の関連するコードブロックは、シルエットを同じようなものにする
- 空行を使って大きなブロックを段落に分ける
- 段落ごとにようやくコメント追加するとコードに目を通しやすくなる
5. コメントすべきことを知る
- コメントの目的は、コードの意図を読み手に理解してもらうこと
- コメントは読み手の時間を奪い、画面を占領するので、コメントにはそれだけの価値を持たせるべき
- コードからすぐにわかることをコメントに書かない
- コメントには自分の考えを記録する(なぜそう書いたか等)
-
- TODO: あとで手をつける, FIXME: 既知の不具合がある, HABK: あまりキレイじゃない解決策, XXX: 大きな問題がある, のようにコードの欠陥にコメントをつけておく
- どこにコメントをつけたらいいか判断する時は、読み手の立場になって考える
- ライダーズブロックを乗り越えるために、うまく書こうとするのではなく、頭にあるコメントをまずはとにかく書き出すことからやってみる
6. コメントは正確で簡潔に
- コメントは領域に対する情報の比率を高くしなければいけない
- どうすればコメントを正確に簡潔に書けるか
- それやこれなどのあいまいな代名詞は使わないようにする
- 関数の動作を明確に記載する
- 引数にはインラインコメントを使う
- 多くの意味が詰め込まれた情報密度の高い言葉を使う
- ex. // 所在地から余分な空白を除去し、AvenueをAve.にするなど整形を施すことで表記がわずかに違う所在地でも同じものであると判別できる。 → // 所在地を正規化する(例:"Avenue" -> "Ave.")
7. 制御フローを読みやすくする
- 条件やループなどの制御フローはできるだけ自然に書く
- 条件式の引数の並び順としては、左側には調査対象を書き、右側には比較対象を書く
- ex. if(length >= 10)
- if/elseブロックの並び順としては、単純な条件や関心を引く条件、目立つ条件を先に書く
- 条件式では否定形よりも肯定形を使用し、肯定形を先に処理する
- 三項演算子によって簡潔に書くことができる場合以外は、基本的にif/elseを使う
- 三項演算子は、if文を省略した書き方ができる
-
条件式 ? 値1 : 値2;
条件式を評価し、trueならば値1、falseならば値2を返す
- ネスト(入れ子)の深いコードは理解しにくいため避ける
- 条件式の引数の並び順としては、左側には調査対象を書き、右側には比較対象を書く
8. 巨大な式を分割する
- コードの塊が大きすぎると理解が難しくなる
- コードを飲み込みやすくするための処理や分割の方法
- 大きなコードの塊を「要約変数」という変数に代入することで管理や把握を簡単にする
- 式の意味を説明してくれる「説明変数」という変数を使う
- 簡潔な名前で式を説明することで、コードを文書化できる
- コードの主要な概念を読み手が認識しやすくなる
- ド・モルガンの法則を使う
- ド・モルガンの法則は、「notを分配してand/orを反転する」ということ
-
NOT(X) OR NOT(Y)
=>NOT(X AND Y)
-
NOT(X) AND NOT(Y)
=>NOT(X OR Y)
-
- ex. if(!(a && !b))は、if(!a || b)になる
- ド・モルガンの法則は、「notを分配してand/orを反転する」ということ
9. 変数と読みやすさ
- 変数が多いと変数を追跡するのが難しくなる
- コードが読みやすくならない不要な変数は削除する
- スコープ内で1度しか使わない変数は、重複コードの削除になっていないので必要ない
- 変数に代入せずとも十分に内容が明確な変数は必要ない
- コードが読みやすくならない不要な変数は削除する
- 変数のスコープが大きいとスコープを把握するのが難しくなる
- 特にグローバル変数はどこでどのように使われるのか追跡するのが難しいので、なるべく使用を避ける
- 変数が頻繁に変更されると現在の値を把握するのが難しくなる
- 永続的に変更されない変数は扱いやすいので、変数は一度だけ書き込むようにする
10. 無関係の下位問題を抽出する
- エンジニアリングとは、大きな問題を小さな問題に分割して、それぞれの解決策を組み立てること
- エンジニアリングの原則をコードに当てはめれば読みやすいコードになる
- 関数やコードブロックを見てコードの高レベルの目標は何か、コードの各行に対して高レベルの目標に直接的に効果があるのか(無関係の下位問題を解決しているのか)を自問する
- 無関係の下位問題を解決しているコードを抽出し別の関数にする
11. 一度に1つのことを
- 一度に複数のタスクを行うコードは理解しにくい
- コードは一度にの1つのことを行うべきである
- コードが行なっているタスクを全て列挙し、タスクをできるだけ異なる関数に分割する
12. コードに思いを込める
- 自分よりも知識が少ない人が理解できるような簡単な言葉で説明する能力は大切
- コードも簡単な言葉で書くべき
- コードをより明確にする簡単な手順
- コードの動作を簡単な言葉で同僚にもわかるように説明する
- 問題や設計をうまく言葉にできない場合、何か見落としているか、詳細が明確になっていないということ
- プログラムを言葉にすることで明確な形になる
- その説明の中で使っているキーワードやフレーズに注目する
- その説明に合わせてコードを書く
- コードの動作を簡単な言葉で同僚にもわかるように説明する
13. 短いコードを書く
- 新しいコードには、テストや保守が必要になるので、コードはできるだけ書かないようにする
- 不必要な機能をプロダクトから削除し、過剰な機能は持たせない
- 未使用のコードや無用の機能を削除する
- 標準ライブラリに慣れ親しんでおく
- 既存のライブラリで問題を解決できることは多い
- ライブラリを再利用できれば、時間の節約にもなるし、コードの量も少なくなる
14.テストと読みやすさ
- テストが読みやすければ、テストが書きやすくなり、テストを追加しやすくなる
- 小さなテストを複数作れば、効率的で、読みやすいものになる
- テストコードの関数にも意味のない名前はつけない
- テストしやすいようにコードを設計できるようになれば、コードの設計が全体的に改善できる
- テストのために本物のコードの読みやすさを犠牲にしてはいけない
- テストがプロダクト開発の邪魔になってはいけない
今後の課題
- 実際にコードを書く際には、今回学んだことを意識して実践する
- 『リーダブルコード』に関しては、今後も読み返す時間を設ける
参照
何かのときにすっと出したい、プログラミングに関する法則・原則一覧
単一責任の原則(SRP: Single Responsibility Principle) - Qiita
オープン・クローズドの原則の重要性について
『リーダブルコード――より良いコードを書くためのシンプルで実践的なテクニック』オライリー・ジャパン