#はじめに
今回『プリンシプル オブ プログラミング』という本を読みましたので、自分なりにアウトプットしていく目的でまとめます。
リンク:『プリンシプル オブ プログラミング 3年目までに身につけたい一生役立つ101の原理原則』
##第1章 前提〜プログラミングの変わらぬ真実〜
###プログラミングに鉄の弾丸はない
プログラミングには「銀の弾丸」に相当するような、万能な技術や決定的手段は存在しない。これは、プログラミングの成果物である「ソフトウェア」が本質的に「困難性」を持っているからである。この「困難性」には4つの性質があり、それが下記である。
-
複雑性
規模が大きくなるほど、ソフトウェア構成要素間の依存関係も非線形に肥大する。 -
同調性
ソフトウェアは単独で存在せず、実世界の様々なものと関係を持ち続ける。 -
可変性
ユーザーの要求によって、変えていく必要がある。 -
不可視性
ソフトウェアは概念の集積であり、原理上不可視である。
以上のように、ソフトウェアは本質的に「困難性」を持っており、問題が多岐に渡るため、特効薬は存在しない。このソフトウェアの「困難性」の最たる原因は「複雑さ」である。歴史を学び、様々な手法や考え方を学んで、複雑さを「軽減」する方法を考える必要がある。
このソフトウェアの本質的な部分である「困難性」を改善するために重要となるのが、「偶有的」つまり副次的な部分をまず改善することである。ここで言う副次的な部分とは、ビルド環境や、プログラミング言語、ライブラリ、フレームワークなどである。これらを自動化したり、より書きやすいものに変更したりすることで、本質的な部分に注力できるようになる。
###コードは設計書である
「基本設計」のみを「設計」とし、それ以下の工程と分けて解釈されることが多いがこれは誤りである。「基本設計」から「詳細設計」「プログラミング」「テスト」「デバック」までのすべてが「設計」であり、これらは不可分のものである。そのアウトプットが設計書である「コード」である。
また、プログラミングは創造的な行為であり、ルーチンワークにはなりえないため、優秀な設計者、プログラマを必要とするものである。また、将来の保守担当者に対する手引書である「ロゼッタストーン」が重要となってくる。このドキュメントには、コードでは表現されない設計理由を記述することで保守担当者の修正の判断材料として有用となる。
###コードは必ず変更される
コードは修正されることを前提に設計する必要がある。リリース後、ユーザーの要望や、要求の変化により機能の拡張や修正を行うことが常であり、また、新規開発中も日々「修正している」と言えるためである。そのために重要となってくるのが「コードが読みやすい」ことである。コードは書く時間より読む時間のほうが長いものであるので、可読性の高いコードを書くことは効率化にも繋がる。
##第2章 原則〜プログラミングのガイドライン〜
###コードをシンプルに保つ
コードを書く際に最も重要なことは「単純性」と「簡潔性」である。これを意識せずコーディングを行うと、コードは複雑化し可読性が下がり、修正やテストもしにくくなる。よりシンプルなコードを書くことが、「修正容易性」に繋がり、開発速度の向上やソフトウェアの長期間に渡る保守を可能にする。
コードの可読性を保つ上で、機能実装のために最もシンプルなコーディングを心がける必要がある。不要な新技術を使用したり、現在必要ではないコードを加えたり、ユーザーからの要件以上の機能を実装するなどの「余計なこと」を行わないことが肝要である。
このような不要な複雑性を避ける考え方のことを「KISSの原則」という。(Keep It Simple, Stupid.などの頭文字)コード自体の可読性だけでなく、ソフトウェアの外部仕様においても、過度な機能や複雑なインターフェースはユーザーにも敬遠されやすくなるため、よりシンプルに保つことが要求される。
また他にも「less is more」「オッカムの剃刀」など、コードや機能の必要以上の複雑性の回避を重要視する考えが存在し、これらはソフトウェア開発、プログラミングにおいて重要である。
###DRY
コードは同じものをコピーして重複させることを避けなければならない。この考えを「DRY」(Don't Repeat Yourself.)という。重複によって、コードの可読性の低下や、修正の煩雑化、また、テストコードが無いために障害のリスクが上がるなどの弊害を生む。
これを避けるためにはコードの「抽象化」が必要である。コードのロジックを「関数化」「モジュール化」することで、コード量が削減され、ロジックやデータに「名前」が付くことによって可読性が向上し、コードの修正や再利用がしやすくなる。
また、コードだけではなく、ソフトウェア開発全体にもDRYの考え方は適用される。テスト・ビルド・リリース作業などの繰り返し作業を自動化することで、品質の安定化や属人化の防止、問題の早期発見に繋がる。
構造化プログラミングやオブジェクト指向プログラミングの技術や、デザインパターンなどの設計手法の多くは重複を排除するための手法を含んでいる。このような、技術や手法は目的を持って編み出されたものであるので、その目的を意識して習得する必要がある。
オブジェクト指向のプログラミングのクラスと、リレーショナルデータベースのテーブル等、異なる抽象化スタイルの境目などのミスマッチのことを「インピーダンス・ミスマッチ」という。このような場合において、やむ終えない重複が発生する場合もある。
繰り返しをしないという指針において、いくらかの用語が存在する。まず「WET」は「DRY」の逆で「Write Every Time」などの略とされ、皮肉的な表現として使用される。また「One Fact in One Place」、「Once and Only Once」など重複を避ける意味合いを持った概念が多くある。
テストの無いコードは「レガシーコード」と呼ばれ、一律に悪いコードであると捉えられている。どれだけきれいなコードであっても、テストがなければ「レガシーコード」であり、不十分と言える。まずは、テストコードを作成することが重要である。
###YAGNI
コードは今必要なものだけを考えて書くべきである。どのような機能が必要になるか予想をしても当たることはまず確率的にありえないため、将来に備えて拡張性を考慮に入れるのではなく、要件として提示されている機能をいかにシンプルに書くかに主眼を置くべきである。汎用性のあるコードは複雑で改修もしづらく、単純なコードのほうが機能拡張の際に労力が小さいことも多くある。
YAGNI(You Aren't Going to Need It)の考え方はコードだけでなく、ソフトウェアの機能にも当てはまる。需要に沿わない豊富過ぎる機能は使い勝手を落としてしまう原因になる。
YAGNIと同様の意味を持つ用語にDTSTTCPW(Do The Simplest Thing That Could Possibly Work)がある。上手く行く方法のうち、最もシンプルな方法で行え、という意味である。シンプルを選択することは難しいことであるが、都度、原則に立ち返る必要がある。
###PIE
コードは書きやすいことよりも、読みやすいことを重視して書くべきである。定義書や設計書は確実に役に立つものでは無く、わかりやすいコードを書いて意図を伝えることが必須となる。コードは書く時間よりも確実に読む時間のほうが長く、多少書く労力が大きくなっても、読みやすいコードを選択したほうが、効率的である。また、実行の効率は後から上げることが難しくないため、よりも読む効率が重視される。コードを読んで直ちに仕組みを理解できないときには、書き直すようにするべきである。読みやすく、障害のない、品質の高いコードやテストコードを書くのには時間がかかるが、欠陥のあるコードは「モグラたたき」のような修正を余儀なくされる。コードの質を上げることが長期的な利益をもたらす。
意図を表すプログラミングの究極の形として「文芸的プログラミング」がある。文芸的プログラミングではドキュメントを記述するための言語がプログラミング言語と結び付けられている。つまりは、コードとドキュメントを分けて書かず、1つの文書に編み込んだものとして書く。これを実現したものが「WEBシステム」である。文芸的プログラミングには4つの利点がある。
- コードの説明や正当性を記述しながらプログラミングするため、こーどについて別の角度から考えるようになる。
- 修正作業がしやすく、コード変更時には説明情報が適切に更新される。
- ドキュメントが1つだけであることが保証される。
- アルゴリズムの説明や正当性の説明など通常、コメントに記述されないこともコードに含まれる。
これらの利点はソフトウェアの保守段階で有用である。文芸的プログラミングはコードを書く際の負担の重さにより普及はしなかったが、PIE(Program Intently and Expressively)のような原則として受け継がれている。
###SLAP
コードを書く際は、高いレベルの抽象化概念と低いレベルの抽象化概念を分離するべきである。関数を抽象レベルに沿って分離し、同じ関数に属するコードの抽象レベルを統一することで「要約性」と「閲覧性」を同時に満たすことが出来る。このようなコードを書くためには、関数の構造化が必要である。各関数は、自信より一段低いレベルの関数を呼び出す処理が中心となりこのような関数を「複合関数」と呼ぶ。「複合関数」はより小さく書くことが求められる。
SLAP(Single Level of Abstraction Principle)はモジュールなどにも適用される。例えば、オブジェクト指向におけるクラス設計であれば、「抽象クラス」と「継承クラス」の関係である。低いレベルの概念は継承クラスにのみ、高いレベルの概念は抽象クラスにのみ存在するよう書くべきである。
SLAP化の作業で重要になるのは「内容を書くこと」と「内容をわかりやすく伝えるための構成を考えること」を別の作業にすることである。
###OCP
コードは拡張が可能且つ、修正によって他のコードに影響を与えない「柔らかい」設計が求められる。これを実現するには、インターフェースを使って設計を行うことが肝要である。クライアントから直接サーバを呼び出すよう設計するのではなく、間にクライアントインターフェースを挟むことで、新機能のサーバが実装された際もクライアントのコード変更が発生しない。
しかし、すべてのコードにOCP(Open-Closed Principle)を適用するのは、冗長性に繋がる。適用のポイントは、変更の内容を予測するのではなく、変化しそうな部分を予測して、設計することである。変化の有りそうなところを見つけ、そこにインターフェースを挟む手法を「流動的要素のカプセル化」と呼ぶ。
また、不安定箇所の変更による影響をインターフェースによって防ぐ手法を「GRASP」(General responsibility Assignment Software Pattern)と呼ぶ。
OCPの実装方法として代表的な技術は、オブジェクト指向の「ポリモーフィズム」である。オブジェクト指向だけでなく、設計手法である「デザインパターン」の多くはOCPを実現させる手段として用いられる。
###名前重要
名前はコードを読む人へのユーザーインターフェースである。各要素に対して適切に名前の付けられているコードは可読性が高くなる。名前を付ける段階で労力が大きくなること以上に、読む効率が上がることを考えて命名することが重要である。
コードを書く際はまず名前を決めることから入るべきである。常に「使う側」「読む側」の視点に立って命名することが重要である。具体例を下に記す。
- より多くの情報を詰め込むようにする
- 名前を「短いコメント」だと考える。
- 誤解されない名前にする
- 他の意味と間違われないか考える。
- 「効果」と「目的」を説明する
- 「手段」には言及しない。
- 処理を書く前にテストを書く
- コードの使用者の視点で考える。
- 発音可能なものにする
- 検索可能なものにする
- 例えば英数字1文字など、ヒット数が膨大になるものを避ける。
また、可能な限り、標準に基づいた用語で命名をし、独自の名前を生成しないことなども心がけるべき点である。
##参考文献
『プリンシプル オブ プログラミング 3年目までに身につけたい一生役立つ101の原理原則』上田勲 著 株式会社秀和システム 2016年3月29日 p.20-61