プリンシプルオブプログラミング ー 1 ~ 4 章 ー

ーー まえがき --

 

■プリンシプル

・プログラミングの指針となる、「前提」「原則」「思想」「習慣」「視点」「手法」「法則」など

 ≒ よいプログラミングのためのエッセンス

・プリンシプルを学ぶことで、技術を習得した際に「なぜ必要なのか?」が分かるため、習得が早く、深くなる。

 

 

ーー 第1章 前提 --

 

■プログラミングに銀の弾丸はない

・プログラミングの成果物であるソフトウェアは本質的に困難(複雑性、同調性、可変性、不可視性)であるため、特効薬はない。

 → ソフトウェア開発の歴史を学び、様々な手法や考え方を学んで地道に複雑さを軽減していく。

 

■コードは設計書である

・プログラミングにおける設計は、「基本設計」「詳細設計」「プログラミング」「テスト」「デバッグ」、製造は「ビルド」「リリース」。

 → 分担するより、全員が行うべき。

 → コードが設計なら、できるだけ早くコードを書き始めなければ不明確な部分ばかりで設計が完了しない。

 

■コードは必ず変更される

・変更に強いコードを書かなければならない。可読性が重要。

 

 

ーー 第2章 原則~プログラミングのガイドライン~ --

 

■KISS( Keep It Simple, Stupid )

・コードには必ず変更が入って無秩序に向かうため、コードの最優先価値を「単純性」「簡潔性」に置く。

・次のことに注意 - 「新しい技術を使いたい」「将来の必要に備えたい」「勝手に要件を加えてしまう」

・コードだけでなくソフトウェアにも適用できる。

・less is more

・「オッカムの剃刀」何かについていくつかの説明が可能なら、もっとも単純なものが正しい

 

■DRY ( Don't Repeat Yourself )

・コードを読む・修正する作業が難しくなる、テストがないので、コードの繰り返しを避け、抽象化する。

 

YAGNI(You Aren't Going To Need It )

・拡張性を考慮してもたいていは外れるので、コードは今必要な最低限にとどめる。

・「DTSTTCPW」Do The Simplest Thing That Could Possibly Work 

  うまくいく方法のうち、もっともシンプルなもので行う。

 

■PIE( Program Intently and Expressively )

・意図を表現してプログラミングせよ

・コードだけがソフトウェアの動作を「正確に」「完全に」知るための手がかりなので、読みやすさを最優先に。

・コードは「What」「How」しか表現できないので、「Why」はコメントする必要がある。それ以外でも適宜行う。

 

■SLAP(Single Level of Abstraction Principle )

・コードに「要約性」と「閲覧性」を与えるために、コードの抽象度を合わせる。

・関数を構造化する。

 

■OCP(Open-Closed Principle)

・コードの変更に柔軟に対応するため、「拡張に対して開いている」「修正に対して閉じている」ことが重要。

・コードにインターフェースを用いて、「コードのふるまいを拡張できる」「コードのふるまいを拡張しても、そのほかのコードは全く影響を受けない」ようにする。

 

■名前重要

・コードを読む人への「UI」なので、命名は最重要課題。

・名前は、

 ーより多くの情報を詰め込む。

 ー誤解されることのないよう気を付ける。

 -「効果」と「目的」を説明する。

 ー自分でチェックする場合、処理を書く前にそのテストを書くようにする。

 -発音可能なものにする。

 -検索可能なものにする。

・「ループバックチェック」名前はその元となった内容の説明を復元できなければならないので、内容の説明文から名前を考えたら、再度説明文を考える。

 

 

ーー 第3章 思想~プログラミングのイデオロギー~ --

 

■プログラミングセオリー

・価値観を技術の選択基準に。

・3つの価値観

「コミュニケーション」「シンプル」「柔軟性」

・6つの原則

「結果の局所化」「対称性」「繰り返しの最小化」「宣言型の表現」「ロジックとデータの一体化」「変更頻度」

・「フォース」ある技術を適用すべき観点

 -解決策が満たすべき「要件」

 -課題に含まれている「制約」

 -解決策に望まれる「特性」など

・「形態は機能に従う」

 

 ー 価値観 ー

 

■コミュニケーション

・コードはコミュニケーションの場である。コードを読む側の視点に転回せよ、

 

■シンプル

・コードの玉石を分ける。本質的な部分を目立つようにし、それ以外の余分な部分がそこに紛れ込まないようにする。

 

■柔軟性

・コードに柔軟性を持たせるために拡張しやすく、かつ、拡張が他に影響しないような設計を心がける。

・ただし、複雑なコードや設計は正当化されないので、即効果のあるコード以外我慢する。

 

 - 原則 -

 

■結果の局所化

・関係性の高いコードを密集させることで、変更の影響が局所にとどまるようにコードを構成する。修正と確認も容易に。

・互いに頻繁に呼び出しあっている関数は本来同一箇所に置くべきかも。

 

■繰り返しの最小化

・大きなコードの塊は、他の大きなコードの塊と同じ部分があるはず。コードを小さな部分に分割して管理。

・修正の影響を局所化するため重複を排除。

 

■ロジックとデータの同一化

・データと操作を同じ場所において見通しをよくする。

・最初からベストな配置は分からないので、後から移し替える。

 

■対称性

・対称性を持ったコードは他の部分も類推できるので、コードに一貫性を持たせる。

・同じことは同じ表現で。

 ー「追加」メソッドがあれば、「削除」メソッドを

 ーあるグループにある関数は同じ引数を取らせる。

 -あるモジュール内のデータは全て同じ生存期間に

 -ある関数内で呼び出す関数の抽象度は全て同じレベルに

 

■宣言型の表現

・命令型ではなく宣言型のコードを取り入れる。 ・・・?

 

■変更頻度

・「変更頻度」コードを修正するタイミングが同じ。「変更理由」が同じということ。同じタイミングで変更される要素は同じ場所に、違うタイミングのものは違う場所に。

・グルーピングされたコード内で変更理由を複数持っているコードの場合、修正に関係ある個所と無関係な箇所が混在するため、関係ない部分まで修正の影響を受けてしまう可能性がある。

・ロジックもデータも変更理由で所属を決める。

・「単一責任の原則」モジュールを変更する理由は1つより多く存在してはならない。

 

 - アーキテクチャ根底技法 -

 

■抽象

・「捨象」「一般化」して複雑さをそぐ。

 

カプセル化

・関連のあるデータとロジックをグルーピングして、1つのモジュールを定義。

・関連のない要素が混じらないため、コードが見やすくなる。

・変更時の影響がモジュール内のみに。

・影響度が明確になるので、コード変更が容易に。

・それぞれが独立した部品になるので、再利用性が高まる。

・小さい単位に分割されるので、複雑な問題に対処できる。

 

情報隠蔽

・クライアントの実装を使用するクライアントから隠ぺいする。

・インターフェースが小さくなり、やりとりがシンプルになり、コード全体の複雑性を下げられる。

・クライアントから見ても、余計な情報が見えないので、モジュールの使い方がシンプルに。

 

■パッケージ化

・モジュールを意味毎にグルーピング。=ソフトウェア全体を意味のある単位に分割すること。

・開発が進む中でボトムアップで行う。

メリット

・ソフトウェア全体がパッケージ単位になるので複雑度が下がる。

・パッケージには関連のないモジュールが混ざらないので、モジュール管理が容易に

・修正時に影響がパッケージ内に留まる可能性が高い。

・依存関係が整理されるので、パッケージ単位で再利用しやすい。

 

■関心の分離

・「関心」ソフトウェアの機能や目的

・関心単位でモジュール化することで、変更が容易、並行開発できる。

 

■充足性、完全性、プリミティブ性

・モジュールが提供する関数が「十分で」「完全で」「純粋な」ラインナップにする。

 

■ポリシーと実装の分離

・ソフトウェアの前提に依存する「ポリシーモジュール」と「実装モジュール」を分離する。

 

■インタフェースと実装の分離

 

■参照の一点性

・変数に対して値の再代入を行わない。

 

■分割統治

例えば、

・ソフトウェア全体設計は、独立して設計できる部分に分割してから取り組む。

・モジュールを設計するときは、「責任・責務」の観点からモジュール分割。

アルゴリズムを設計するときは、マージソートのように分割して解決できないか

 

 - アーキテクチャ非機能要件 -

機能面以外の全般についての要件で、リリース後の開発や保守、運用、リソースの効率活用に大きな影響を及ぼすので、アーキテクチャ設計の際には非機能観点も 考慮に入れなければならない。

 

■変更容易性

・容易に改善できるようにして、長期間使えるソフトウェアを作る。

・「保守性」変更箇所の局所化で、障害時に修正しやすく。

・「拡張性」モジュール間の結合度が弱く、新機能の追加、モジュールの置き換えや除去が容易に行える。

・「再構築」モジュール間の関係の再構築

・「移植性」ソフトウェアを他の言語や実行環境で行えるように設計する。

 

■相互運用性

・標準規格を選択して、他のソフトウェアと連携できるようにする。

 

■効率性

・コンピュータリソースを最大限引き出せるようアーキテクチャを設計する。

・「間接化」モジュール間の直接の結合を避けるため、それらの間に介在する「媒介モジュール」を導入すること。ただし、「効率性」とのバランスを考慮する。

 

■信頼性

・「冗長化」「フェールソフト」「フェールセーフ」を行う。

 

■テスト容易性

・テストの品質が本体の品質であるので、テストを容易にするアーキテクチャが求められる。

・テストも考慮した本体設計が必要なので、テストのためのコードが本番にあってもよい。

 

■再利用性

・再利用することで開発効率を上げるため、アーキテクチャの構成を、既存の構造やモジュールにプラグインできるようにする。

・再利用の3の法則

「難易度3倍の法則」再利用可能なモジュール開発は、単一のソフトウェアで使うモジュールを開発する場合に比べ3倍難しい。一般化した問題の処理を考えなければならないため

「テスト3種類の法則」3つの異なるソフトウェアでテストする必要がある。実際に使われてみるまで分からないため。

 

 ー 7つの設計原理 ー

コード価値観が「漏れない」「ぶれない」ため、コードレビュー時、あるいはコードを作りこむときに考慮すべき観点。

 

■単純原理

・シンプルで自然なコードを書く。

 

■同型原理

・一貫性のあるコード書く。

 

■対称原理

・コードの形に対称性があると、予測がつきやすく、コード理解のスピードが速まる。

 

■階層原理

・抽象階層構造のあるコードを書く。

 

■線形原理

・可読性、保守性、保守性が向上するので、処理の流れは直線にこだわる。

・そのためには分岐の少ないコードを書く。

 

■明証原理

・ロジックが明瞭なコードを書く。

 

■安全原理

・必然性のないところや曖昧なところは安全サイドにコードを書く。

・要件や機能の仕様書は必要条件なので、エラー時の処理、ログ出力など十分条件を満たすコードは個々のプログラマによる。

 

 - UNIX思想 -

1969年に生まれ、いまなお使われているUNIXの設計思想。

 

■モジュール化の原則

・モジュールのインタフェースを狭くして、関係性の高いもののみ集める。

 

明確性の原則

・コードを読むとき、分かりにくいコードの「解読」を3回以上繰り返してはいけない。2度目に読まなければならないとき、修正あるいはコメント等の対策すべき。

 

■組み立て部品の原則

・ソフトウェアを相互接続できるよう、データストリームを受けつけ、加工し、別のデータストリームとして出力するフィルタにして組み立てる。

・テキストストリームを読み書きするコマンドラインで使用できるソフトウェアを設計する。

 

■分離の原則

・ソフトウェアの前提に依存し不安定な「ポリシー」と独立し安定的な「メカニズム」を分離する。

・分離したポリシーを改善する。

 

■単純性の原則

・コードはシンプルに。シンプルを美しいとする文化に。

 

■倹約の原則

・コードの分量も複雑度も大きなコードを書かない。

・まず小さなコードを書く。大きくなったら分割。

 

■透明性の原則

・設計時に下記2点を意識。

「透明性」ソフトウェアの動作が、一目で何をどうしているかが分かる。

「開示性」ソフトウェアの内部状態にすいて、監視、表示できる。

デバッグ機能を設計の最初の段階から組み込む。

 

■安全性の原則

・ソフトウェアを堅牢にするため、コードを「透明」かつ「単純」にする。

・そのためにコードレビューや、特異な入力や極端に大きな入力に耐えられるか検証する。

 

■表現性の原則

・データはロジックよりも扱いやすいため、避けられない複雑さはロジックよりもデータに寄せる。

 

■驚き最小の原則

・予想通りのインタフェースを設計する。

・よく似たソフトウェアのインタフェースをモデルにする。

・想定ユーザーの特徴を考慮する。

・伝統に注意を払う。

・「一見似ているが異なる」ということを避ける。

 

■沈黙の原則

・表示を最小限のみに抑える。

-本当のエラーだけを標準エラー出力に表示。

デバッグの目的で、進行状況についてメッセージを表示した場合はそれ用のスイッチ。

 

■修復の原則

・修復失敗時は処理停止。エラー通知は大きく。

 

■経済性の原則

プログラマの時間節約のため開発環境のためのハードウェアやソフトウェアに投資する。

 

■生成の原則

・コードジェネレータを作る。

 

■最適化の原則

・早い段階での最適化は、透明性や単純性が犠牲になり、半端な最適化が全体最適化の妨げになるので、まず正しいコードを書いてから最適化を行う。

・プロトタイプをまず作ることは有効。

 

■多様性の原則

・唯一の正しいやり方は存在しないので、選択の受容性を許容し、よりよりやり方を求め続ける。

 

■拡張性の原則

・ソフトウェアを接続可能な設計にし、その用途などを明記する。

 

 - UNIX哲学 -

UNIXの背後にある設計の哲学。UNIXという考え方。

 

■小は美なり

・小さく作って小さく保つ。

ー理解が容易、保守が容易、マシンリソースに負担をかけない、他のソフトウェアと組み合わせやすい。

・大きなソフトウェアは複雑でコードの理解が困難、不測の事態に対応できない。

 

■1つ1つ仕事

・1つのソフトウェアは1つの仕事に集中することで、コードの不要部分がなくなる。

 

■即行プロトタイプ

・プロトタイプをできるだけ早く作ることで、前提の誤りを早期に発見でき、要件不備による手戻りを減らせる、早いうちから誤りを取り除く作業を始められる。

 

■効率性や移植性

・ハードウェアに依存しないコードを書く。

 

■データはテキスト

・バイナリではなくテキストで。ユーザーにもプログラマにも優れている。

CSVやHTMLを選択して、他との接続制を高める。

 

レバレッジ・ソフトウェア

・既存のソフトウェアをできるだけ利用する。

・単機能で単価値に集中したソフトウェアを作り、それらをグルー言語で組み合わせる。

 

シェルスクリプト活用

シェルスクリプトによって、梃の効果と移植性を高める。

・グルー言語はシェルスクリプトを用いる。

 

■対話インタフェース回避

・コマンドインタプリタに制御を返す設計

対話インタフェースのデメリット

・ソフトウェアごとに独自の対話方法が必要になる。

・ソフトウェア同士が対話できなくなる。

・待ち時間が多くなる。

・入力部分の解析コードが大きく、醜くなる。

・「大は美なり」的なアプローチになる。

 

■フィルタ化

・「フィルタ」入力ストリームをデータとして受け取り、何らかの加工を施し、加工したデータを出力ストリームに送り出すこと。

・ソフトウェアとはフィルタである。

・データの入出力には標準入出力、エラー情報は標準エラー出力を使用する。これによってソフトウェア同士が接続可能になる。

 

 

ー 視点 プログラマの観る角度 ー

 

■凝集度

・モジュールに含まれている機能は純粋に。混じりけのあるモジュールは脆い。

・凝集度の高いモジュールは

 ーコード設計の明確さと理解のしやすさが高まる。

 ーコードの保守と拡張が容易になる。

 ーコードの再利用性が促進される。

 ーモジュール間の疎結合性も同時に促進される。

 

■結合度

・モジュール間は疎遠に。結合な密なモジュールは、互いに依存し、影響し合うので様々な問題が発生してしまう。

・モジュールの結合度を下げるには

 ーデータの受け渡しは引数で

 ーデータの置き場所にグローバル変数をできるだけ用いない。

 ー渡す値に応じて動作が変わるようなコードを書かない。

 

■直交性

・コードは独立させよ。

・モジュール間の結合度を下げることで実現可能。

 

■可逆性

・問題があったときにやり直しができる設計にする。

・コードに柔軟性を持たせる。特定技術に依存するのはやめ、コードを独立して変更しやすい状態にする。

 

■コードの臭い

・コードの中で、理解しにくい、修正しにくい、拡張しにくい、と感じられる部分を適切なコードに改善していく。

・似たコードをよく見るとか、長すぎるとか、大きすぎるとか、多すぎるとか、名前が合わないとか。

 

■技術的負債

・問題コードを管理する。一時的に負債となるコードを書いても、リリース後に再度修正する。暫定ソリューションをそのままにしない。