プリンシプル オブ プログラミング
読んで思ったこと
多くの原則が記されており、その多くに共通することとして「プログラムの保守性」が挙げられていることから、保守性の高いプログラムが求められていること、保守性の高いプログラムを作ることは一筋縄ではいかないことが分かった
プログラムを書くときに重要な原則の知識は付いたが、それを活かすために下記の事柄を順次学習していく必要がありそう
オブジェクト指向プログラミング
設計手法
アーキテクチャ
デザインパターン
メモ書き・感想
ソフトウェアの可変性
ソフトウェアを計画通りに作り上げる事ができても、それを使っていくうちに新たな要求が発生する
万能の解決策は存在しない
1つのツールや技法が何にでも当てはまるわけではない
歴史を学び、様々な手法や考え方を学ぶ事で、複雑さを軽減することはできる
ソフトウェアにおけるコードは設計書
ハードウェアの場合
まず設計を行い、アウトプットされた設計図を元に製造を行う
ソフトウェアの場合
「基本設計」→「詳細設計」→「プログラミング」→「テスト」→「デバッグ」までが「設計」
リリースビルドが「製造」に当たる
製造はプログラマではなくコンパイラやビルドシステムが行う
出来上がったコードは「設計書」になる
コードは「ドキュメント」である
コードは修正されるもの
最初のリリースで要望を完全に網羅したソフトウェアを作るのは難しい
使っていくことで改善点や新たな要求が浮かび上がる
最初のコードに固執してはいけない
変更に強いコード
変更される前提で書く
読みやすいコード
書く時間より読まれる時間の方が長い
型をつける、チーム内でルールを決める、linterを導入する、1行で書くことに拘らない、が想像できる
プログラミングのガイドライン
KISSの原則
Keep It Simple, Stupid. または、Keep It Short and Simple
「シンプルにしておけ、愚か者よ」、または、「簡潔かつ単純にしておけ」
コードを書くうえで「単純性」と「簡潔性」を最重要に考える
コードは書けば書くほど自然と複雑になる
気にせずに書き進めるとやがて誰も理解できないコードになる
「スパゲティーコード」
ドキュメントとしての役割を果たせない
シンプルなコード
構成する各要素がすべてシンプル
各要素が担う債務が最小限
要素同士の関連がシンプル
読みやすく理解しやすく修正しやすくテストしやすい
「修正しやすさ」、「テストしやすさ」を常に考えられればシンプルなコードに近づけると思った
DRYの原則
Don't Repeat Yourself.
繰り返すな
コピペをしてはいけない
最小限のコードを必要なところから呼び出す
繰り返し使う文字列や数値は定数にいれる
片方のコードを直すときにもう片方のコードも直さなくてはいけなくなる
コードに限ったことではない
DBスキーマ、テスト、ビルドシステム、ドキュメントなども対象
「ソフトウェア開発全体において情報を重複させない」
抽象化することで重複を排除する
【疑問】抽象的なコードとは?
YAGNIの原則
You Aren't Going to Need It.
それはきっと必要にならない
必要なコードは必要になったときに書く
あとで必要になるかもしれない...で書かない
単純性 > 汎用性(再利用性、拡張性)で考える
単に今日のことだけを考えた、汎用性の低いコードを書く。と言うことではなく、単純なコードが結果として汎用性が高くなるのではないかと思った
PIEの原則
Program Intently and Expressively.
意図を表現してプログラミングせよ
コードを読めば書いた人の意図が伝わるコードを書くこと
コードを読むのはコンパイラではなく人
意図を伝えるコメント
What: 何をしているか、How: どのようにしているか、だけではなく、Why: 「なぜそれをしているか」が重要
Whyがあれば読む人がわかりやすいと言うこと
理想はコメントで説明しなくても読んで伝わるコード
SLAPの原則
Single Level of Abstraction Principle.
抽象化レベルの統一
コードの水準を合わせること
code:高水準.js
function bizlogic() {
logic1();
logic2();
logic3();
}
他の関数を呼び出すだけで計算などの処理は行わない
code:低水準.js
function logic1() {
// 何らかの処理
}
function logic2() {
// 何らかの処理
}
function logic3() {
// 何らかの処理
}
それぞれ1つだけの処理を実装する
高水準・低水準の2つだけに分類されると言うわけではない
各関数は自身より1段低いレベルの関数を呼び出す処理が中心となる
「名前で意図を伝える」ことが重要
1行だけの関数を作っても良い
何が嬉しいか?
高水準で宣言的に書かれた関数が処理の要約となり、コードが読みやすくなる
処理の階層を俯瞰でき、構造が理解できる
OCP
Open-Closed Principle
オープン・クローズドの原則
拡張に開いていて、修正に閉じている
コードの振る舞いを拡張しても他のコードには影響を及ぼさない
作成したコードやクラスが、いかに他のコードに迷惑をかけることなく機能拡張できるかの指標・迷惑をかけないやり方
名前重要
Naming is important.
コードにおいて命名は最重要課題である
良い命名の為に
処理を書く前にテストを書くことでそのコードが何をする者なのかが明確になりやすい
結果の局所化
変更の影響が、局所に止まるようにコードを書くということ
→モジュール化
関係性が高いコードをまとめる
関係性が高いコードをまとめてモジュールかする
ロジックとデータの一体化
ロジックと、そのロジックが操作するデータを近くに配置すること
Reactのコンポーネントが該当するのではと思った
宣言型
段階を指示せず、論理で指示する
結果を得るための「どうやって」は指定しない
"〜なもの"とい言う形で処理の結果として欲しいものを記述する
言語例: SQL
命令型
結果を得る為の「どうやって」というアクションまで書き込む
for / if / while 文などをつかって処理をコントロールする
順番のある命令列を書き連ねることで結果を得る
言語例: JavaScript,ruby,Javaなど
関心の分離
MVCモデル
Model
DBとのやりとりと、ロジックを書くべき部分
View
見た目
railsなどのMVCフレームワークではHTMLを生成する部分
フロントエンドとされる部分
ReactはMVCのViewのみに関心を持つ
Controller
ModelとViewの橋渡し
アスペクト指向プログラミング
プログラム中の様々な対象に適応できるようなコードを書く手法
ロギング
様々なオブジェクトで共通して現れるが、単一のオブジェクトやメソッドとして切り出して定義することができない
横断的機能(横断的関心事)
ポリシーと実装の分離
ポリシーモジュール
ソフトウェアの前提に依存する
他のモジュールに対する引数の選択を行う
ソフトウェアごとに異なる
ソフトウェアに更新が生じるとポリシーモジュールも更新が強いられる
ポリシーモジュールと実装モジュールが混ざっているとポリシーの変更に実装がひきづられることになる
実装モジュール
ソフトウェアの前提に依存しない
独立したロジック部分
他のソフトウェアで再利用することもできる
インタフェースと実装の分離
インタフェースでモジュールが持つ機能を定義し、モジュールの使用方法を定める
「実装」パートは使用者からアクセスできない
使用者はインタフェースだけ知っていれば良い
コードのシンプルさ、見やすさ > パフォーマンス
パフォーマンスは後から肉付けしてけば良い
コードの対称性、命名の対称性
何かを「追加する」機能がある場合、何かを「削除」する機能も必要
set / get, start / stop, begin / end, push / pop など
階層原理
code:good.js
function parent() {
getResource(); // リソース確保
// リソースを使って何かを行う処理
releaseResource(); // リソース解放
}
function getResource() {
// 何らかのリソース取得の処理
}
function releaseResource() {
// リリース解放処理
}
code: NG.js
function parent () {
getResource(); // リソース確保
}
function getResource() {
// 何らかのリソース確保の処理
// リソースを使って何かを行う処理
// リソース解放処理
}
必要な情報だけを出力する
必要なエラーを選定する
「起動中」、「終了します」など不要なメッセージはなくすべき
エラーの回復に失敗したら処理は止めるべき
凝集度
モジュールに含まれる機能の純粋さ
レベルが高いほど純粋で強く良いとされるモジュール
レベル7: 機能的強度
モジュール内の全ての命令が1つの機能を実行する為に関連し合っているモジュール
そのモジュールで1つの機能を遂行する為に全ての命令がお互いに関連している
レベル6: 情報的強度
特定のデータ構造を扱う複数の機能を1つのモジュールに集めたもの
入口点を複数個持ち、各入口店は単一の機能を実行する
レベル5: 連絡的強度
基本的には手順的強度の特性と一緒
モジュール内機能間でデータの受け渡しをしたり、同じデータを参照する
レベル4: 手順的強度
問題を処理する為に関係している複数個の機能のうちのいくつかを実行する
順番に実行される
モジュールの機能のどれか一つだけ必要となったときには使えない
レベル3: 時間的強度
特定の時点に連続して事項する複数の機能を1つのモジュールにまとめたもの
機能間に強い関連性はなく、ただ特定の時点に連続して実行しているだけ
レベル2: 論理的強度
ある機能を抽象的に捉えてまとめたもの
例: 全ての入出力操作をまとめたモジュール
例: いろいろなデータを編集するためのモジュール
関連したいくつかの機能を含み、そのうちの一つだけが呼び出しモジュールによって識別されて実行される
モジュール内の全ての命令が実行されるわけではない
レベル1: 暗号的強度
モジュール内の要素間に特別の関係が認められない
ここでの暗号とは、「偶然に物事が一致する」と言う意味
コードの統治
細かくモジュールに分割する
分割されたモジュールはそれぞれ独立していることが重要
それぞれのモジュール内での関連性が最大になること
モジュール間の関連性が最小になること
最適化
システムのパフォーマンス(速さ)をあげること
reactでいえばuseMemo, useCallbackなどを使えばパフォーマンスはよくなる
最初のコーディングでいきなりパフォーマンスを意識しない
コードを最適化するにつれてコードの質が落ちる可能性がある
読みづらく、修正しにくくなる
「よいコード」を書いていれば自然と最適化されている
それに追加で必要な箇所を最適化すれば良い
コードにバリケードを設ける
特定のインタフェースを「安全地帯への境界線」として使用する
インタフェースのを通過するデータを検証して適切な処置をとる
GUI,CUI,ファイル,他システムからのデータをバリケードを使って検証し、内部モジュールで安全に使う
アサーションとエラー処理の区分け
想定外のエラーにはアサーションで対応し、想定内のエラーには適切なエラー処理で対応する
バリケードの外側ではデータに何かを想定するのは危険
「エラー処理」で対応する
バリケードの内側に通ってくるデータは想定通り
不正なデータを検出したらデータではなくコードに問題がある
コードの実行の真偽をコード自体で検査する「アサーション」を適応する
上田勲. プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の原理原則 (Japanese Edition) (Kindle の位置No.439). Kindle 版.