この度「プリンシプルオブプログラミング」という書籍を読みましたので勉強のためまとめます。
第1章 前提 プログラミングの変わらぬ事実
プログラミングに特効薬はない
プログラミングには特効薬はなし。
ソフトウェアは4つの困難性を持つ。
- 複雑性
- 数千万行もあるコードは珍しくない。
- ソフトウェア構成要素間の依存関係は規模が大きくなるほど非線形的に増大する。
- 同調整
- ソフトウェアは実世界に同調し続ける必要がある。
- ソフトウェアはハードウェアやネットワーク、他のソフトウェア、人間の行動や習慣など、実世界の様々なものと関係を持つ。
- 可変性
- ソフトウェアを計画通りに作成しても、それを使ったユーザーから新たな要望が出される。
- ソフトウェアは常に変化し続ける。
- 不可視性
- ソフトウェアは概念の集積で目に見えるものではない。
- 抽象化して図面にすることはできるが、情報を捨象するため全部を表現することはできない。
コードは設計書
コードこそが設計書である。
プログラミングは設計行為で、ソフトウェア開発で作成されるあらゆる文書の中で、エンジニアリングのドキュメントと言えるのはコードのみ。
ハードウェア製品の改良が「設計」で行われるのと同じようにソフトウェアの改良は設計書であるコードで行われる。
コードが設計であるならばコードをできるだけ早く書き始めるべき。コードを書かないと不明確な部分だらけでいつまでも設計が終わらない。
コードは必ず変更される
コードは一度書いてそれで終わりではない。
リリース後にユーザーから要望があり、機能を拡張する場合がある。
ユーザー側も実際に使用してみるまでわからないため、最初のリリースだけで要望を完全網羅したソフトウェアを作るのは不可能。
プログラミングにおける1つ1つの判断は、「そのコードが変更される」という前提で行う必要がある。
結局コードは書いている時間よりも読んでいる時間の方が長くなるので「コードが読みやすい」ということが一番大切。
読みやすいコードを書くのに時間がかかったとしても、読む時間を短縮できれば元をとることができる。
第2章 原則 プログラミングのガイドライン
KISS(Keep It Simple, Stupid)
コードを書くときは最優先の価値を「単純性」「簡潔性」に置く。新規に書くときだけでなく、バグ修正や機能の拡張でも常に複雑にならないことを意識してコードをシンプルに保つ。
コードは修正するにつれて、無秩序に複雑化していってしまうもの。最終的には改善しようにもできない「腐った」コードに行き着く。
シンプルなコードは、構成する各要素が全てシンプルで、各要素が担う責務が最小限に抑えられている。シンプルなコードは読みやすく、理解しやすく、修正が容易にできる。
シンプルなコードを書くにはコードから余計なこと、過剰なことをできるだけ排除する。
余計なことをするケースとして以下のことが考えられる。
- 新しく覚えた技術を使いたい
- 技術を覚えてそれを使用したいがために、不要にトリッキーなコードを書いてしまうことがある。
- コードは頭の良さをアピールする場ではなく価値を提供するもの。
- 本当にその技術が必要なのかを吟味する。
- 将来の必要に備えたい
- 将来必要になるかもしれないという理由で過剰なコードを書いてしまうことがある。
- ほとんどの場合、今不要ならそれは必要にならないので必要なときに必要なぶんだけコードを書くことが重要。
- 勝手に要件を加えてしまう
- 勝手に要件を判断し、余計なコードを加えてしまう場合がある。
- 要件を決めるのはユーザーであり、プログラマが勝手に要件を決めてはいけない。
- 不要なコードを入れると保守にかかる手間と時間が雪だるま式に増えてしまう。
KISSの適用範囲
KISSはコードだけでなく外部使用にも適用できる。
機能ばかり多く、複雑なソフトウェアは使われなくなる。
機能、インターフェースをシンプルに保つことで使いやすく使われるソフトウェアになる。
DRY(Don't Repeat Yourself)
コードのコピペは厳禁。
コードの重複で最も多いのは、ひとまとまりのロジックを安易に別の部分にコピペして使用して時で同じロジックが複数箇所にばらまかれてしまう。
「定数リテラル」を直接コードに埋め込んであることも、コードの重複にあたる。
コードに重複があると、バグ修正や機能追加などコードを改善することが難しくなる。
具体的には以下の困難が発生する。
- コードを読む作業が難しくなる。
- 同じコードがあると量的に「より多く」なり、質的に「より複雑」になる。
- コードを修正する作業が難しくなる。
- 同じようなコードが複数あると、そのロジックを変更したときに複数箇所で変更が必要になる。
- 修正漏れの危険性がある。
- テストがない
- 重複しているようなコードは大抵の場合レガシーなコードであり、テストがない。
- 一念発起して修正してもテストがないと新たな障害を生む。
重複をしないためにはコードを抽象化することが大事。
コードのロジックを抽象化するためには、処理のまとまりに名前をつけて「関数化」「モジュール化」する。
データであれば、それに名前をつけて定数を定義する。
抽象化することで以下のメリットがある。
- コードの量が減り、読む量を減らせる。
- ロジックやデータに名前がつくことで、コードが読みやすくなる。
- 同じコードが1つに集約され、修正箇所が1箇所で済む。
- 抽象化した部分は再利用しやすくなる。
抽象化には時間もリスクもかかるが、長い目で見ると有利になる。
DRYの適用範囲はコードに留まらず、ソフトウェア開発に関わる活動すべてに適用できる。
ソフトウェア開発における作業には、「同じことの繰り返し」が多くある。この作業もDRYを適用する。
具体的には繰り返しの作業を自動化する。
継続的インテグレーションと呼ばれるソフトウェアのテスト・ビルド・リリース作業が自動化の対象となる。
やむを得ないDRY違反について
ソフトウェア開発にはある程度やむを得ない重複も存在する。
インピーダンス・ミスマッチ。
ソフトウェアの世界でミスマッチが発生しやすいのは異なる抽象化スタイルの境界。
その最もたる例は、オブジェクト指向プログラミングのクラスと、リレーショナルデータベースのテーブル。リレーショナルデータベースとそれを扱うプログラミング言語があったときにそのミスマッチを埋めるためには、リレーショナルデータベース側に「テーブル定義」、コード側に「テーブルマッピング設定ファイル」「ソースファイル」と3箇所に同じ情報をもつことになる。
YAGNI(You Aren't Going to Need it)
コードは「多分必要になる」という理由で書いてはいけない。
本当に必要になったときに、必要なものだけを書くべき。
いろいろな自体に備えてコードを盛り込んでも、結局利用されないことがほとんど。
拡張性を考慮に入れると、コードに「余計な」複雑性を盛り込むことになるため難解で保守しにくいコードができる。
コードは今必要なものだけを書き、凡庸性よりも単純性を考える。
YAGNIの適用範囲
YAGNIはコードの話に留まらずソフトウェアの機能にも当てはまる。
豊富な機能は一見魅力的に映るが、予想で作った「的外れの」機能はつかわれないばかりか、全体の使い方を複雑にして使い勝手が悪くなる。
PIE(Program Intently and Expressively)
コードを書くときに大切なのは、意図を明確に表現するように書く。
ソフトウェアの動作を完全に知るための手がかりはコードのみでドキュメントでは正確に知ることができない。
「要件定義書」はどんなものが欲しいかが書いてあるのみで、「基本設計書」はどのようなソフトウェアで要件を実現するかが書いてあるのみ。そして「詳細設計書」はどのような構造でソフトウェアを作るのかの予定が書いてあるだけ。
結局ソフトウェアの動作を把握するためには、コードを読むしかない。
コードを書くときには、書きやすさより読みやすさを重視する。なぜならコードは書かれることより読まれることの方が多い。
コメントを書く
コメントがなくても読めるようなわかりやすいコードを書くのが理想的であるが、コードは「何をしているか」「どのようにやっているか」までしか表現できない。
「なぜそれをしているか」を表現するにはコメントを使用する必要がある。
SLAP(Single Level of Abstraction Principle)
コードを書く時は高いレベルの抽象化概念と低いレベルの抽象化概念を分離するようにする。
機能の複雑さに応じて多階層に分離し、それら各々のそうにおいて、自分のそうに所属する抽象レベルを揃える。
抽象レベルが揃うとコードは優れた書籍のようになる。
function 高水準(){// レベル1の目次
中水準1();
中水準2();
}
function 中水準1(){// レベル2の目次-1
低水準1();
低水準2();
}
function 低水準1() {// 本文内容
// 処理
}
function 低水準2() {// 本文内容
// 処理
}
function 中水準2(){// レベル2の目次-2
低水準3();
}
function 低水準3() {// 本文内容
// 処理
}
コードがレベルが揃った関数に分類されていると、「要約性」と「閲覧性」を同時に満たすことになる。
関数の一覧は目次のようになり、要約性持つ。分割された関数は小さなコードの塊になり閲覧性がよくなる。
SLAPの適用範囲
SLAPは関数に限らず「モジュール」などにも適用される。
優れたソフトウェア開発には、概念が複数のレベルに分離されてそれらは異なる入れ物に格納される。
例えば、オブジェクト指向におけるクラス設計だと、この入れ物は「抽象クラス」とその「継承クラス」になる。抽象クラスに高いレベルの概念を持たせ、その継承クラスに低いレベルの概念をもたせることでSLAPが実現できる。
OCP(Open-Closed Principle)
コードの変更は波及させない。
コードは、「拡張に対して開いている」「修正に対して閉じている」という。
拡張に対して開いているとは、コードの振る舞いを拡張できるという意味。
修正に対して閉じているとは、コードの振る舞いを変更しても、その他のコードは全く影響を受けないという意味。
2つの属性を同時に満たすように設計すると既存のコードにまったく影響を与えず、機能を追加できるようになる。
ソフトウェアは常に変化し続けるもの。
コードは変更に対して柔軟に対応できる「柔らかい」設計が必要で、「柔らかい」設計とは「拡張に対して開いている」「修正に対して閉じている」設計のこと。
変更に対して柔軟に対応できない「硬い」設計をしてしまうと、コードの少しの変更でさえ依存関係を持つ全ての箇所に影響を与えてしまう。
コードにインターフェースを用いる
ある機能を持ったモジュールを設計するときは、モジュールの使用者であるクライアントがモジュールの提供者であるサーバーを直接呼び出すと、別のサーバーを使用したい場合はクライアントを変更しないといけないので「硬い」設計になる。
なのでクライアントとサーバーの間にはモジュール使用者のためのクライアントインターフェースを設け、クライアントがサーバーの入れ替わりによってコードを変更しなくてもよくする。
OCPの適用範囲
コードの全部にOCPを適用するのはやりすぎで、変更が発生しなかったら複雑になるだけの無駄で冗長なコードになる。
OCPの過度な適用を防ぐには変更の内容を予測しすぎないことが大事。
ある程度割り切って、実際に変更が発生するのを待つことも考える。
OCP適用のポイントは変更の内容を予測するというより変化しそうな部分を予測すること。
名前重要
プログラミングにおいては「命名」を最重要課題として認識し慎重に取り組むこと。
適切な名前がつけられたということはその要素が正しく理解されて、正しく設計されているということ。
名前はコードを通じてプログラマ同士がコミュニケーションするための最大の場となる。
適切に命名されたわかりやすい名前の関数は以下のメリットを受けられる。
- コードを読んでいるときに、関数名を見ただけでその先の処理の概要が把握できる。
- コードを書いているときに、その関数を使用する場面においては名前に導かれ目的や使用用途がすぐに理解できる。
コードはまず名前から決める
- 名前には多くの情報を詰め込むようにする。名前を短い「コメント」だと考える。
- 名前は誤解されることのないように気をつける。
- 名前は「効果」と「目的」を説明し、手段には言及しない。
- 名前を自分自信でチェックしたい場合、処理を書く前にそのテストを書くようにする。
- 名前は発音可能なものにする。
- 名前は検索可能なものにする。英数字1文字などはNG。
第3章 思想 プログラミングのイデオロギー
プログラミングセオリー
プログラミングの最大の関心事は「拡張方法が多く存在し、余分な要素が存在せず、読みやすく、理解しやすい」最高のコードを作り上げること。
最高のコードを実現するためのセオリーとして以下の3つの「価値」がある。
- コミュニケーション
- シンプル
- 柔軟性
これらの価値がより良いコードを目指すための、あらゆる決定を左右するものとなる。
「価値」は実際のプログラミングに使用するには抽象的すぎるので価値をプログラミングの実戦につなげるための「橋渡し」の概念が必要。
架け橋となる6つの原則
- 結果の局所化
- 繰り返しの最小化
- ロジックとデータの一体化
- 対称性
- 宣言型の表現
- 変更頻度
コミュニケーションについて
コードは人にも見せる「ドキュメント」である。文書の本質はコミュニケーションツール。
プログラミングにおいてコミュニケーションが良好であることはコードを読んだ人が、コードを理解し、コードを修正し、コードを使用することができるということ。
ソフトウェアの開発コストの大半は最初に開発された後に発生する。つまり保守のコストが多い。
そのコストを節約するためにコードは読みやすくし、コードを通じてプログラマどうしが円滑にコミュニケーションする。
コードで良好なコミュニケーションを取るには、コードを書いているときに、他の人のことを考えるようにする。
他の人はこのコードを見てどう感じるだろうか?と考える。
シンプルについて
コードがシンプルであるとは、コードから余分な複雑性が取り除かれた状態を指す。
コードの複雑性とは、コードをどうにか動かそうとして格闘した痕跡による複雑さを表す。
シンプルである状態を保つためには玉石を見分け、それぞれ明確にわかるようにしておくことが大事。本質的な部分を目立つようにして、それ以外の余分な要素がそこに紛れ込まないようにする。
柔軟性について
コードにおいて柔軟性とは、コードの変更の容易さを表す。
コードの変更はいずれ必ず発生するので、変更のやりやすい柔軟なコードをを作成しておくことは必須事項。
コードに柔軟性を持たせるために拡張しやすくかつ、その拡張がほかに波及しないような設計を心がける。
結果の局所化について
結果の局所化における結果とは、「変更の影響」のこと。
結果の局所化とは「変更の影響が、局所に留まるようにコードを構成する」ということ。
モジュール化はこの目的のために生まれた技術の1つでモジュールはモジュール変更の影響をモジュール内に留めることを目標の1つにしている。
結果の局所化において重要なのは関係が濃密なコードをまとめること。
関係性の高いコードを密集させ関係性の低いコード同士が依存しないようなコードにする。
お互いに頻繁に呼び合っているようなモジュールは本来一緒にいるべき要素が、分散している可能性がある。
繰り返しの最小化
繰り返しとは「重複」のこと
関数などは重複したロジックを関数化して1つにまとめて共有コードとして使用できる。
重複を排除するためにはコードを、たくさんの小さい部品に分割する。
大きなコードの塊は、別の大きなコードの塊のどこかと同じ部分を含んでいる可能性が高いので、大きな塊を分解すると共通項が導きやすくなる。
コード内で「完全に同じところ」「とりあえず似ている」「全く異なる」部分がそれぞれどこなのかを明確に伝える
ロジックとデータの一体化について
「ロジックとデータの一体化」とは「ロジック」と「ロジックが操作するデータ」を互いに近くに置くようにすることを表す。
コードを修正するときにロジックとそのロジックが操作するデータは大抵の場合は同じタイミングで変更することになるのでそれらが同じ場所にあったほうが好ましい。
どのロジックとデータを近づけるべきかは最初からベストな解はわからないので、いったん仮配置して後から調整していくのが効率的。
対称性について
「対称性」とは、一般的には変換に関して普遍である性質のことを指す。
プログラミングにおける「対称性」とは「同じ考えなら、コードのどの場所で現れても同じように表現される」ことを表す。
コードを書くときに同じ種類のことは同じように表現することが重要でそのためには以下のことを意識する。
- 「追加」メソッドがあれば、対になる「削除」メソッドを作成する。
- あるグループにある関数は、同じ引数を取るようにする。
- あるモジュール内のデータはすべて生存期間が同じであるようにする。
- ある関数内で、呼び出す関数の抽象度は同じレベルとする。
宣言型の表現について
「宣言型の表現」とはコードの意図を伝えようとするときにできるだけ「命令型」よりも「宣言型」で表現すること。
命令形のプログラミングは「問題の解決」すなわちデータ構造とアルゴリズムを記述する。
逆に宣言型のプログラミングとは、「問題の定義」すなわち解くべき問題の性質や、その際に満たすべき制約を記述する。
宣言型のコードには順序や条件分岐が存在せず純然たる事実が宣言的に書かれているのでコードが読みやすい。
宣言型の汎用プログラミング言語の代表的なカテゴリに関数型言語がある。汎用言語ではないがHTML、CSS、SQLも宣言型。
変更頻度について
「変更頻度」とはコードを修正するタイミングが同じだということで、これは「変更理由」が同じということ。
同じタイミングで変更される要素は同じ場所に置き、異なるタイミングで変更される要素は別の場所に分けておき変更理由でグルーピングする。
アーキテクチャ根底技法
アーキテクチャ根底技法とはよいソフトウェア、アーキテクチャ構築のための基礎原理。
以下の10の技法がある。
- 抽象
- カプセル化
- 情報隠蔽
- パッケージ化
- 関心の分離
- 充足性、完全性、プリミティブ性
- ポリシーと実装の分離
- インターフェースと実装の分離
- 参照の一点性
- 分割統治
抽象について
抽象とは概念的に明確な「線引き」を行うこと。
線引きに従って、あるモジュールをそれ以外のモジュールから明確に区別する。
抽象は「捨象」「一般化」という2つの観点からまとめられる。
「捨象」は複雑な対象のいくつかの性質を捨て去り、特定の性質に目を向けること。
「一般化」は具体的な対象から共通の性質を抽出し、より汎用的な概念に定式化すること。
抽象化は優れた設計を構築するためのプログラマの基礎技術。
複雑な対象に取り組むときは「捨象」して、余計なものを捨てて本質を捉えるようにする。
異なる複数の対象に取り組むときは「一般化」して共通する性質を見つけ出し、共通点を組み合わせて汎用的な概念を構築する。
カプセル化について
関連のあるデータとロジックをグルーピングして、1つのモジュールを定義する。そして関連性の強いデータ郡とロジック郡をモジュールという膜で包み込むことをカプセル化という。
カプセルに入れるかのように関連のある要素のみをモジュールに入れて閉じ込め、関係のない要素と混ざらないようにする。
そうすることで関連性の強いデータからなるデータ構造と関係性の強いロジックからなる関数軍を持つ、純粋で質の高いモジュールが作成される。
情報隠蔽について
モジュールの実装を、そのモジュールを使用するクライアントから隠蔽して必要のないものは見せないようにする。
そうすることでインターフェースが小さくなり、クライアントとのやりとりがシンプルになる。クライアントから見ても余計な情報が見えないためモジュールの使い方がシンプルで使い勝手が良くなる。
モジュールはシンプルな機能のみを公開し、内部状態や内部機能は全て隠蔽する。情報隠蔽にはカプセル化を使用。
カプセル化と情報隠蔽の違い
-
カプセル化
- 関連性のある要素を集めてモジュール化すること
- 関連の深いデータと関数を1箇所にまとめる
-
情報隠蔽
- モジュールの内部状態や内部関数を隠蔽すること
- 内部に対する外部からの直接アクセスを遮断する
パッケージ化について
パッケージ化はモジュールを意味のある単位にまとめグループ化し、ソフトウェア全体を意味のある単位に分割すること。
パッケージ化には以下のメリットがある。
- ソフトウェア全体が、パッケージという小さい単位に分割されるので複雑度が下がる
- パッケージ内は、関連のないモジュールが混じらないのでモジュールの管理がしやすくなる
- 修正に対して影響度がパッケージ内に留まる可能性が高いのでコードが変更しやすくなる
- 依存関係が整理されることになり、パッケージを単位とした再利用をしやすくなる
パッケージ化はモジュールがある程度できてから、ボトムアップで設計する。トップダウンでは決めることができないので注意。
開発が進みモジュールが増えていくとともにパッケージの設計を始めるようにし、プログラミングの進行とともに進化させていく。
関心の分離について
「関心」とはソフトウェアの機能や目的のこと。
関心を分離するとは、それぞれの関心に関連するコードを集めて独立したモジュールとし他のコードから分離するということ。
コードの変更は関心を単位に発生するのでコードが関心ごとに分離されていると以下のメリットを受けられる。
- 関心ごとに独立して修正できるので読む範囲が限られ変更が容易になる。
- 影響範囲が関心内に留まるので変更時の品質が安定する。
- コードを書くときは関心を単位として開発するので分業して並行して開発を進めることができる。
関心を分離するときは、関心ごとにモジュールを作成して互いに異なる責務や無関係な責務を分離する。
MVCパターンでは「ビジネスロジック」「ユーザーへの表示」「入力処理」を分離している。
充足性、完全性、プリミティブ性について
カプセル化によって関連のある要素がモジュールに集まり、そのモジュールの担っている抽象化の表現は「充足」「完全」で「プリミティブ」であるべき。
-
充足性
- 充足性とは、モジュールが表現しようとしている抽象が、それを伝えるために十分であるかということ。
-
完全性
- 完全性とはモジュールが表現しようとしている抽象が、すべての特徴を備えているかということ。
-
プリミティブ性
- プリミティブ性とはモジュールが表現しようとしている抽象がすべて純粋であるかどうかということ。
モジュールが表現している抽象を明確にするために「十分で」「完全で」「純粋な」ラインナップにする。余計なものがあるときは削除するか、別のモジュールに移動する。
ポリシーと実装の分離について
モジュールは「ポリシー」または「実装」を扱い、どちらか一方のみを扱う。両方扱ってはならない。
-
ポリシーモジュール
- ソフトウェアの前提に依存するビジネスロジックや、その他のモジュールに対する引数の選択を行う部分。
-
実装モジュール
- そのソフトウェアの前提に依存しない、独立したロジック部分で、前提条件についてはモジュールに対する引数として与えられる。
ソフトウェアの前提に依存する部分の「ポリシー」と依存しない部分の「実装」を意識して設計する。
インターフェースと実装の分離について
モジュールは「インターフェース」パートと「実装」パートの2つの分離した部分から構成する。
-
「インターフェース」パート
- 「インターフェース」パートとはモジュールがもつ機能を定義し、モジュールの使用方法を定める部分。
- クライアントによってアクセス可能な関数のシグネチャから構成される。
-
「実装」パート
- 「実装」パートとは、モジュールが持つ機能を実現しているコード部分で、モジュールが内部で使用するロジックとデータが含まれている。
- 「実装」パートはクライアントからアクセスができない。
「インターフェース」と「実装」を分離することでモジュールの使用者であるクライアントが、実装の詳細を知る必要がなくなりシンプルでわかりやすくなる。
参照の一点性について
モジュールの要素について、宣言され定義されるのは1回限りにする。
定義が1回ということは変数の値を初期化したら以後は値を変更しないということ。
これによって変数の値を追いかけなくても済むコードになる。
変数に対して再代入を行わないことを意味する「単一代入」を意識する。
分割統治について
そのままでは解決が難しい「大きな問題」は、いくつかの「小さな問題」に分割して個別に解決する。
小さくした問題は、元の問題より格段に解決が容易になる。
問題を分割するときは以下のようにする。
- ソフトウェア全体を設計するときは、独立して設計できる部分に分割してから取り組む。
- モジュールを設計するときは、「責任・責務」の観点からモジュールを分割するようにする。
- アルゴリズムを設計するときは、マージソートのように、ボトムアップで分割してから問題解決できないかを検討する。
- 大量データ処理を設計するときは、MapReduceのように計算を小さい単位に分割して、分散環境で並行して実行できないかどうかを検討する。
アーキテクチャ非機能要件
非機能要件とは、機能面以外の全般についての要件のこと
非機能要件の観点には以下のようなものがある。
- 変更容易性
- 相互運用性
- 効率性
- 信頼性
- テスト容易性
- 再利用性
アーキテクチャ設計で非機能要件を考慮に入れるために以下のように取り組む。
- 要件定義において、それぞれの観点についてどの程度必要とされるのかを確認する。
- 開発において、アーキテクチャ設計の時点で、この要件を考慮に入れた構造を考える。
- テストにおいて、要件を満たしているか確認する。
機能のテストは「何をするのか」に着目するのに対して、非機能のテストは「どのように動作するか」に着目する。
変更容易性について
変更容易性とは、そのソフトフェアがどれだけ容易に改善ができるかの能力のこと。
ソフトウェアは絶え間なく変更と拡張が行われる。ユーザーの要求に長期間に渡ってきめ細やかにすばやく対応するためにもコードの変更が容易であるアーキテクチャが必要になる。
アーキテクチャの設計は、「保守性」「拡張性」「再構築」「移植性」の側面から考察するようにする。
-
保守性
- 保守性とは問題点の解決、すなわち障害が発生したコードの修正のしやすさのこと。
- 保守性を高めるには、変更を局所化し、他のモジュールへの副作用を最小にするアーキテクチャを採用する。
-
拡張性
- 拡張性とは新しい機能の追加、モジュールの新しいバージョンへの置き換え、不要な機能やモジュールの除去などのしやすさを指す。
- 拡張性を持つためには、モジュール間の結合度が弱いことが必要。
-
再構築
- 再構築とはモジュール間の関係の再組織化を行うこと。
- モジュールを異なるサブシステムに移植するなど一の変化を行う場合に再構築が必要になる。
-
移植性
- 移植性とは、ソフトウェアを様々なハードウェアプラットフォーム、ユーザーインターフェース、オペレーティングシステム、プログラミング言語、コンパイラに適合させる際の、やりやすさのこと。
- 移植性を高めるにはハードウェア依存性を考慮しながらソフトウェアを設計する必要がある。
相互運用性について
相互運用性とはソフトウェアが他のソフトウェアとやりとりできる能力のこと。
他のソフトウェアと連携できるということは、既存の資産をそのまま活かせるということで新規で開発するソフトウェアが少なくなるメリットがある。
相互運用性のあるソフトウェアを作るためには標準規格を選択する。
プロトコルやデータ形式の選定において、業界の標準的な規格を選択し、標準に準拠していれば将来に渡ってソフトウェアの価値が持続する。
効率性について
効率性とはソフトウェアが実行に伴うリソースの使用において適切な性能を引き出す能力のことで観点として以下の2つに分かれる。
-
時間効率性
- 時間という観点から、リソースの使用効率を定義する。
- 「スループット」「レスポンスタイム」「ターラウンドタイム」などで計測する。
-
資源効率性
- コンピュータ資源という観点から、リソースの使用効率を定義する。
- CPUの使用時間や、メモリ使用量、ストレージ消費量、ネットワーク伝送量などで計測する。
リソースは限られているのでソフトウェアはそれを効率よく利用する必要がある。
リソースは適切に利用し、あるものはすべて有効活用してソフトウェアの性能を最大限引き出す。
信頼性について
信頼性とはソフトウェアが例外的な場面、予期しない方法や不正な方法で使用された場面においても機能を発揮する能力のこと。
信頼性には以下の2つの側面がある。
-
フォールトトレランス
- ソフトウェアに障害が発生したときに、正常な動作を保ち続ける能力。
- 例外の発生に対して正しい振る舞いを保証し、内部的には修復を行う。
-
ロバストネス
- 不正な使用方法や入力ミスから、ソフトウェアを保護する能力。
- 様々な使用方法に対して、システムとして定義された状態に移行する。
ソフトフェアごとに求められる信頼性は異なる。
大規模システムはサービスの停止は許されないが、個人用のソフトウェアはそこまでのサービス持続性は求められない。
要件をあらかじめ明確にして、それに沿ったアーキテクチャ設計を行わなくてはならない。
フォールトトレランスの観点の対策としては、アーキテクチャに内部的な冗長性をもたせるようにする。
障害発生時には提供する機能を絞り込み大事な機能のみを提供して処理継続を優先する設計も考慮する。
ロバストネス観点の対策としては、障害時にその部分を切り離す設計を検討する。
そもそも障害が発生しないようにあらかじめユーザーが誤った操作をしても安全に可動させる設計も考慮する。
テスト容易性について
テスト容易性とは、ソフトウェアに対して「効果的」かつ「効率的」にテストを行う能力のこと。
テストが効果的とはテストが深く、質が高いことで、ソフトウェアの隅々まで漏らさずその品質を検証できること。
テストが効率的とはテストのコストや労力が少ないことを意味する。
アーキテクチャの策定の時点から、検証方法の観点も含めて設計すると良い。
テストコードが本番コードに従属するのではなく、テストのためのコードが本番にあっても良いくらい。
再利用性について
再利用性とはソフトウェアを全体でも一部でも別のソフトウェアの開発に再利用できる能力のこと。
再利用性には「再利用するソフトウェア開発」「再利用のためのソフトウェア開発」という2つの側面がある。
-
再利用するソフトウェア開発
- プロジェクト内の既存モジュール、以前のプロジェクトのモジュール、各種ライブラリなどを利用することを意味する。
- 再利用可能な成果物を開発中のソフトウェアライブラリにそのまま、あるいは形成して統合する。
-
再利用のためのソフトウェア開発
- 将来のプロジェクトで再利用できるようなモジュールを、現在のソフトウェア開発で創出することを意味する。
- 他のソフトウェアに再利用されるためのソフトウェアを開発する。
再利用するソフトウェア開発の場合はアーキテクチャの構成を既存の構造やモジュールに「プラグイン」できるようにする。
開発のニーズに既存モジュールを適合させ、既存モジュールを糊付けするためのモジュールを実装する。
再利用のためのソフトウェア開発をする場合、開発中のソフトウェアから自己充足的部分を取り出すことがアーキテクチャにする。自己充足部分は、変更を加えずに他のシステムで再利用できることが求められる。
7つの設計原理
7つの設計原理とは障害を作り込まないために考慮すべき、コード構造上の7つの核心観点のこと。
実際のソフトウェア開発現場の経験則から導出された、品質の良いコードのための「経験則」。
この原理はコードレビュー時のときにチェック観点として使用する。
- 単純原理
- 同型原理
- 対称原理
- 階層原理
- 透明原理
- 明証原理
- 安全原理
- 線形原理
ソフトウェアの品質を担保するための手法としてコードレビューは有効であるが、レビューに一定の価値観や観点がないと、指摘が散漫になる。
この7つの設計原理を共通の価値観としてコードレビュー時に使用すると良い。
単純原理について
単純原理とは、「シンプルにこだわる」という原理のこと。
バグは複雑なところに出るのでシンプルで見通しの良いコードを心がける。
高等なテクニックを使うことなく、単純なやり方で通すようにする。
同型原則について
同型原理とは「形にこだわる」という原理のこと。
同じことはが同じように表現されていると、逆に「違うもの」が目立つようになる。
違うものはバグの要因となりやすい部分でコード上の違和感に気づくようになり障害を見つけやすい。
コードには一貫性を持たせ、スマートさや独創性を捨てる。
対称原理について
対称原理とは「形の対称性にこだわる」という原理のこと。
ある処理について「対」になるものを考える。
例えば、あるフラグが立っている処理があったら落ちている処理もあるはず。
「条件」があれば「反条件」にもこだわる。
制御条件には必ずそれと対になった反条件がある。制御条件に整合性を持たせるために反条件の整合性も検討してみる。
また、例外的な状況は特殊なケースであるために対称性を崩し、障害の温床になりやすいのでコードから例外的状況をできるだけ排除するようにする。
階層原理について
階層原理とは「構造が階層であることにこだわる」という原理のこと。
階層ごとに処理を取り決め、同じ種類の処理が異なる階層間に渡らないことが重要。
例えば、排他制御のフラグを立てたら、同じ構造でフラグを落とす。
コードに明確に表現された階層構造があると、読む人が全体を抽象化して理解することができ、必要に応じて階層を下りより詳細なレベルを把握できるようになる。
コードの各々について、抽象レベルを意識して、階層構造を構築する。1つの階層は同じ抽象レベルのものだけで構成する。
線形原理について
線形原理とは「処理の流れは直線であることにこだわる」という意味。
ある機能が、いくつかの機能の重ね合わせによって実現されているのがシンプルで良い構造。
逆にスイッチでコードを制御したり、条件の数をむやみに増やしたりすると、コードがわかりにくくなる。
処理の流れが直線的に読めるようなコードにするためには、処理の条件分岐を少なくする。
そのためには、特殊な振る舞いを主処理に混ぜて書かないようにする。
明証原理について
明証原理とは「ロジックの明証性にこだわる」という原理のこと。
明証性とは、ハッキリと証明すること。つまり、一見して明らかに正しいと言えるようなコードを書くことを表す。
明証であることに手段は選ばず、コードだけで明証できない場合はコメントを書いたり別途ドキュメントを作成して図を書くなどして正しさを証明する。
ロジックが明瞭であるコードを書くためにはロジックを直感でわかりやすいものにする。コードを読んでいる人が疑問に思うようなことは排除するかコメントしておく。
安全原理について
安全原理とは「安全性にこだわる」という原理のこと。
必然性のないところや曖昧なところは安全サイドで設計・プログラミングしておく。
ありえない条件をあえて考慮してコードを書くということを意味する。
ありえないという条件でもあえて考察の対称として設計・プログラミングしソフトウェアが安全に動作する可能性を高くする。
UNIX思想について
UNIX思想とはUNIX文化の中で育まれた優れたプログラミングを行うための経験に基づいた実践的な「技」の集合で17個の原則にまとめられる。
- モジュール化の原則
- 明確性の原則
- 組み立て部品の原則
- 分離の原則
- 単純性の原則
- 倹約の原則
- 透明性の原則
- 安定性の原則
- 表現性の原則
- 驚き最小の原則
- 沈黙の原則
- 修復の原則
- 経済性の原則
- 生成の原則
- 最適化の原則
- 多様性の原則
- 拡張性の原則
UNIXは1969年に生まれ今なお使われ続けており、とてつもない生命力がある。
多くの種類のハードウェアに使われており、使われ方も多様。
UNIXが成功した理由は、初期のUNIXプログラマが最初の段階で下した設計判断の正しさにあり、その設計判断のエッセンスがUNIX思想。
モジュール化の原則について
ソフトウェアは複雑であるが、複雑さの度合いを下げることはできる。
クリーンなインターフェースで結び付けられたシンプルなモジュールたちでソフトウェアを組み立てるようにする。
モジュールは余分な物を削ぎ落として極限まで少なくする。
クリーンなインターフェースを持つシンプルなモジュールはあまり他のモジュールと関係を持たなくなり問題を局所化できる。
明確性の原則について
コードは「巧妙に」ではなく「明確に」なるようにする。
テクニックでシステムのパフォーマンスをあげても、複雑さを大幅に引き上げてしまうならダメ。
複雑なコードは読みにくく、その部分で障害が発生する。
すぐに理解でき、めったに壊れない明確なコードを書くようにすること。
組み立て部品の原則について
ソフトウェアは他のソフトウェアと組み合わせることができるように作る。
これはソフトウェアをできる限りシンプルなフィルタとして作るということ。
フィルタとは入力としてデータストリームを受け付け、加工し、別のデータストリームとして出力するソフトウェアのことでデータストリームの形式はテキスト形式が望ましい。
分離の原則について
メカニズムからポリシーを切り離す。
-
ポリシー
- そのソフトウェアの前提に依存する部分のこと。コードの中では比較的に不安定であり、ビジネスロジックやユーザーインターフェースなどがそれにあたる。
-
メカニズム
- そのソフトウェアの前提に依存しない、独立した部分のこと。コードの中では比較的に安定していて、描画処理のラスタ操作やアルゴリズムなどのエンジン的な役割を果たす部分がそれにあたる。
ポリシーとメカニズムを切り離すことで以下のメリットが得られる。
- メカニズムを壊さずに、新しいポリシーを試せるようになる。
- メカニズムが独立しているので、メカニズムの有効なテストを書きやすくなる。
- メカニズムが独立していれば他のソフトウェアで再利用が可能。
単純性の原則について
コードが単純になるように設計する。
コードが複雑化するのは以下のような原因がある。
- 技術を誇示しようというプログラマの気持ち
- 技術を誇示しようとして設計を複雑にしてしまい、チームのプログラミング力やデバッグ力を超えてしまうことがある。
- 機能に対する外部からの要求
- 誰も使わないが、カタログスペックは上がるような「機能」を求める販売側の圧力によって優れた設計が崩壊させられることがある。
プログラマは「もっとも単純で美しい」ことを誉めあう文化を構築することが大事。
コードが膨れ上がり複雑化することを拒否し、大量の機能でソフトウェアを飾り立てることに反射的に抵抗していく。
倹約の原則について
コードの分量的にも内部の複雑度的にも「大きい」コードは書かないようにする。
コードを書くときには小さく書くことを心がける。
もし、コードを継ぎ足していくうちに大きくなってしまったら分割し、分割できないときのみ大きなコードを残すことにする。
透明性の原則
ソフトウェアの動作は外からわかりやすく見えるように設計する。
そのためには以下の「透明性」と「開示性」を意識して設計をする。
-
透明性
- ソフトウェアの動作を一目ですぐに「何をどのようにしているのか」が理解できること。
-
開示性
- ソフトウェアの内部状態について、監視または表示できること。
このような設計をしてコードを見える化するとデバッグや障害調査が簡単になる。
デバッグは開発期間の大きな割合を占めるので早い段階でデバッグ簡単にするための仕組みを取り入れておくと良い。
安定性の原則について
ソフトウェアが安定しているとは予想外の条件下でも適切に動作するということ。
しかし予想外の条件も考慮しているとコードが複雑になる傾向があるので、結果逆に障害が発生する可能性が高くなるというジレンマがある。
安定したソフトウェアにするには、内部構造を簡単に説明することができるようなものにする必要があり、「透明性」と「単純性」を満たすコードを書くようにする。
-
透明性
- コードを見通すことができて、何が起きているのかがすぐにわかるようなこと
-
単純性
- コードで行われていることが複雑でなく、全ての分岐条件を難なく説明できること
コードが「透明性」「単純性」を持つためにも以下のことを意識する。
-
コードレビュー
- コードを書いたプログラマがコードの内部構造について正しく説明できなければ危険信号
-
特異な入力や客端に大きい入力に耐えられるような検証
- これには他のソフトウェアから与えられる入力を使用すると、効果的なテストになる。
- 人間の手入力に加えて自分のソフトウェアに効率的にストレスをかけることができる。
表現性の原則について
コードにおける情報の表現方法は、データに寄せて書くようにする。
情報をデータ側に固めることによって、ロジック側が読みやすく安定したものになる。
データ構造であれば複雑なものでも簡単にモデリングでき、より簡単にデータを表現することができる。
データ構造、もしくはロジックのどちらかを複雑にしないといけないときは、迷わずデータ構造が複雑になる方法を選択する。
コードを改善するときも、コードの複雑さをデータの複雑さに切り替えていくことを検討する。
驚き最小の原則
インターフェースはそれを使う人が想像するであろう形にする。
つまり使う人の驚きを最小にするように設計する。
ユーザーの思ったとおりに動作するソフトウェアはストレスなく使える。
予想通りに動くインターフェースを設計するために以下のことを意識する。
-
よく似たソフトウェアのインターフェースをモデルにする
- ユーザーがよく使っていて、機能がよく似たソフトウェアのインターフェースを真似する。
-
想定ユーザーの特徴を考慮する
- エンドユーザーなのか、他のプログラマなのか、システム管理者なのかといったユーザーのタイプによって驚きが小さいインターフェースは異なる。
-
伝統に注意を払う
- UNIXの世界には、コンフィグレーション、実行制御ファイルの形式、コマンド行スイッチなどにお馴染みの慣習がある。
- 伝統が築かれたことには、学習を容易にするという理由がありこれらの伝統を学んで仕様の策定に活かす。
-
一見似ているが微妙に異なるということを避ける
- 馴染みがあるものは既存のものであるという期待を高めるだけに、裏切られたときの失望が大きくなる。
- ほとんど同じものを作るくらいなら全く異なるものを作ったほうがユーザーにとっては親切。
沈黙の原則について
ソフトウェアは表示を最小限に抑えて、寡黙に仕事を進めるようにする。
ソフトウェアを使うユーザーにとってもソフトウェアに接続するソフトウェアにとっても相手が寡黙な方が付き合いやすくなる。
なので重要な情報のみ出力して、内部動作の情報を混在しないようにすること。
これを行うための方針が以下の2つ。
- 本当のエラーだけ標準エラー出力に表示して、その他の要求されてないデータは一切出力しないようにする。
- デバッグの目的で、進行状況についてメッセージを表示したい場合は、冗長モードのスイッチを作りデフォルトではそれを無効にしておく。
修復の原則について
ソフトウェアの動作中にエラーの回復に失敗したのであれば、処理は継続すべきではない。
そのエラーについては、できるだけ目立つようにする。
エラーが発生したときはできる限り早い段階で、けたたましく通知するようにする。
ソフトウェアが自分で回復できないときは、無理して処理を継続するのではなく、ユーザーに早く気づいてもらい判断してもらうことが重要。
経済性の原則について
プログラマの時間は貴重で高価なリソース。
以下のような問題が、プログラマの時間を浪費する。
-
ハードウェアが貧弱
- 開発マシンが貧弱で、容量不足、CPUの性能不足、メモリ不足、モニタが小さいなど。
-
使用するソフトウェアに対する制限
- 必要なソフトウェアが購入できないと何かしらの工夫をして同じことを実現しないといけない。
-
環境に関するルールや制限
- インターネットで開発に必要なサイトなのにアクセスできない、クラウドで可動するソフトウェアが使用できないルールなど。
ハードウェアやソフトウェアの購入を抑制するのは経費削減のためであるが、それによって開発スピードを落とし生産性が落ちては本末転倒。
生成の原則について
コードを書くためのコードを書く。つまりコードを生成するコードを書く。
可能な範囲で適材適所で部分的にもコードを生成するようにする。
特に、繰り返しが多く定型的なコードはコードジェネレータの対象とする。
最適化の原則について
プログラミングにおける最適化とはパフォーマンスチューニングを行うことで、動作スピードを上げたり、メモリやディスクのスペースなどのリソースを効率化すること。
最適化する前に正しく動作するコードを書くことが前提。
まずは正しく動作するコードを作成してから速くすることを肝に銘じる。
多様性の原則について
ソフトウェアにおいてその選択の多様性を受容する。
ソフトウェアに関わるすべての場面において「唯一の正しい方法」は存在しないので多様性を認め、思考停止することなくより良いやり方を模索し続けることが重要。
拡張性の原則について
コードには成長の余地を残しておくべきで、拡張性を考慮した設計を行う。
ユーザーの要望は絶えず変化するのでソフトウェアが役に立ち続けるにはソフトウェアが成長し続ける必要がある。
ソフトウェアは拡張のための接続部を柔軟なものにして、コードの中に「〜が必要になったら」というコメントを残しておく。
ただし拡張性の考慮は必要でない機能を追加するための免罪符ではないので注意。あくまで機能を簡単に追加するためのコードを書いておくということ。
UNIX哲学
UNIX哲学とは、UNIXの背後にある設計の哲学のこと。いわば「UNIX」という考え方。
以下の9個の定理としてまとめられる。
- 小は美なり
- 1つ1つ仕事
- 即行プロトタイプ
- 効率性より移植性
- データはテキスト
- レバレッジ・ソフトウェア
- シェルスクリプト活用
- 対話インターフェース
- フィルタ化
「小は美なり」について
小さいソフトウェアは美しい。美しいとは「価値が高い」ということ。
小さいソフトウェアのメリットは以下の6つ。
-
理解が容易
- コードがシンプルで最小限のアルゴリズムしか含まずすべてが目的の作業に直結している。
-
保守が容易
- コードが読みやすいと保守もやりやすくなる。
-
マシンリソースに負担をかけない
- 実行イメージがわずかなメモリしか専有しないのでメモリの割当が容易になり、スワップやページングといった作業が減り性能が向上する。
-
他のソフトウェアと組み合わせやすい
- 小さいゆえ、仕事内容もインターフェースもシンプルなので他のソフトウェアとの自由な組み合わせによって、要求の多様性や変化に柔軟に対応することができる。
従ってソフトウェアは小さく作り、小さく保つようにする。
ソフトウェアを小さくするには解決する問題をきちんと理解することが大事で、多くの場合問題を理解していないがために巨大な解決策を考え、巨大なソフトウェアになってしまう。
「1つ1つ仕事」について
1つのソフトウェアには1つのことを上手くやらせる。
最良のソフトウェアとはその生涯においてだた1つのことを上手く成し遂げるソフトウェアのことで、1つのことを上手く成し遂げたら直ちに退場して、次の1つのことを上手くやるソフトウェアに道を譲る。
1つの仕事に集中することにより、コードの不要な部分をなくすことができるシンプルになる。
なので1つのソフトウェアには1つの仕事を担当させるようにする。
もし問題が大きかった場合は問題を小さく分割してから、その小さな問題に対応する小さなソフトウェアを作成する。
「即行プロトタイプ」について
できるだけ早くプロトタイプを作成すること。
あるアイデアがものになりそうか、目に見えるうようにして現実的かどうかを確かめるには試しに作ってみるのが一番。
早いプロトタイプ作成によってリリースを早める。プロトタイプ作成が早いほど製品のリリースは早まる。
プロトタイプ作成によって、以下のメリットが得られる。
-
前提の誤りを早期に発見できる。
- プロジェクトの初期においては設計の方針を定めてその方針の可否を早めに判断しなければならない。
- プロトタイプを作成すると前提の誤りを早期に発見し、わずかな被害で済ませることができる。
-
要件不備による手戻りを減らせる
- プロトタイプのフィードバックにより、要件不備による手戻りを減らすことができる。
- プロトタイプをユーザーにみせて良い反応が得られれば要件が間違っていないことが確認できる。
-
早いうちから誤りを取り除く作業を進められる。
- プロトタイプのフィードバックにより早いうちから誤りを取り除く作業を始められる。それが早ければ早いほど高品質の最終製品に早くたどりつくことができる。
「効率性より移植性」について
ソフトウェアの開発は選択の連続であり、中でも難問が「移植性」「開発効率性」という二律背反の選択であるが、これは「移植性」を重視すべき。
移植性とは、他のプラットフォームに合わせてコードを書き換えるときにコストが少なくて済むようなソフトウェアの能力。
移植性に重点をおいたソフトウェアは新しいハードウェアへの移植コストが少なくて済むので、余った時間を新機能の開発に向けることができる。
移植性のあるソフトウェアを作成するためには、ハードウェアに依存する部分と依存しない部分を切り離した設計を行う。
「データはテキスト」について
データはテキストファイルに保存し、バイナリ形式のデータファイル設計は採用しない。
データは移動して、交換して、様々なソフトウェアに使用されてその価値を高める。
テキストファイルには以下のメリットがある。
- もっとも一般的で移植性が高い形式で、データファイルはコードよりポータブルなのでより移植性が高まる。
- 人間がデータをただちに確認でき、普通のテキストエディタで観覧可能で、修正することも容易。
- ツールやコマンドが扱いやすい形式で、バイナリ形式のように別のバイナリ形式への変換を気にする必要はない。
テキストファイルの中でも言語やオペレーティングシステムによらない標準的な形式であるCSVやXMLなどを選択すると他のソフトウェアとの接続性が高まる。
「レバレッジ・ソフトウェア」について
レバレッジ(梃子)とは棒と支点からなる力を増幅する装置のこと。
ソフトウェア開発は梃子として利用することで、自分の力を何倍にも増幅できる装置となり、梃子の力を得るには支点をいかにして自分の方へ近づけることができるかが課題。
ソフトウェアにおいてのそれは既存のソフトウェアを再利用しながらソフトウェアとソフトウェアを組み合わせること。個の力を組み合わせてテコの原理で増幅させる。
コードを大量に書く一番効率がいい方法は借りてくることで、コードを借りるとは他のモジュールや設定ファイル、あるいはソフトウェアそのものを自分のソフトウェアで使うということ。
ソフトウェア開発で梃子を利用するためにも機能満載のソフトウェアを書くのではなく、それぞれが単機能で単価値に集中した小さなソフトウェアを作り、それらをグルー言語でつなげ大きな仕事を成し遂げる。
「シェルスクリプト活用」について
シェルスクリプトによって梃子の効果と移植性を高めることができる。
-
梃子の効果
- ソフトウェアの梃子を最大限利用するために、シェルスクリプトを効果的に使う。これはシェルスクリプトを使うことで他のソフトウェアやコマンドをつなぐことができる。
-
移植性
- ソフトウェアの梃子の効果を確実にするためにシェルスクリプトを利用して移植性を高める。シェルスクリプトはインタプリタでプラットフォーム専用のバイナリにコンパイルする必要がないのでコンパイル言語に比べて移植性が高い。
シェルスクリプトをグルー言語として使用し、小さなソフトウェアをシェルスクリプトでつなげて、大きな仕事を成し遂げる。
ちなみにグルー言語の「グルー」とは糊のことでグルー言語はソフトウェア同士を結びつけることを主眼とした言語のこと。
「対話インターフェース回避」について
過度の対話的インターフェースを避ける。対話的インターフェースは「拘束的ユーザーインターフェース」とも呼ばれる。
ユーザーはそのソフトウェアのユーザーの内部に拘束され、拘束を解くための行動を起こさない限り、そこから逃れることはできない。
対話的インターフェースは以下のような問題を持つ。
-
ソフトウェアごとの独自の対話方法を覚えないといけない
- 本来UNIXではコマンドインタプリタの使い方だけ覚えればいいが、各ソフトウェアごとに異なる独自の使い方を覚えなければならない。
-
ソフトウェア同士が対話できなくなる
- インターフェースが人間に最適化され、シェルスクリプトによる梃子が使用できない。
-
待ち時間が多くなる
- 人間の入力待ちがボトルネックになりマシンが力を発揮できない。
-
入力部分の解析コードが大きく、醜くなる
- 人間の入力可能性を考え、人間にわかりやすい出力を考慮すると必然的に入力文字列解析のコード量は膨らむ。
-
「大は小なり」的なアプローチになる。
- 対話的インターフェースでは機能選択に「メニュー」が使用されるが、これが「多機能主義」のトリガーになる。
以上の理由から基本的に対話インターフェースを避けるように設計を行う
あくまでソフトウェアの会話の相手は人間ではなく、ソフトウェアを中心に考えるようにする。
1つの仕事をしたらコマンドインタプリタに制御を返すようなソフトウェアを作成する。
「フィルタ化」について
すべてのソフトウェアはフィルタとして設計する。
フィルタとは入力ストリームをデータとして受け取り、何らかの加工を施して加工したデータを出力ストリームに送り出すこと。
ソフトウェアの本質はデータを処理することで生成することではないので、ソフトウェアをフィルタ化して設計する。
そのためにはコマンド起動できるソフトウェアの場合は標準入力出力の取り扱いを正しく設計する必要がある。
データの入力に標準入力、データの出力に標準出力、エラー情報には標準エラー出力を使用するような設計をするとソフトウェア同士が接続可能になる。
UNIX哲学と小定理
UNIX哲学には定理ほどではないが、その一翼を担う小さな定理が10個ある。
- 環境カスタマイズ
- ユーザが自分好みに応じて、自分で環境を調整できるようにする。
- 軽薄短小カーネル
- オペーレーティングシステムのカーネルは小さく軽くする。
- 少文字使用
- 命名には少文字を使い、かつ短くする。
- 森林保護
- 紙を使わないようにして、森林を守る。すべてのテキストファイルはコンピュータに保存する。
- 沈黙は金
- ソフトウェアは極力表示出力を抑えるようにする。
- 並列思考
- 並行して考えるようにする。
- 「並行」して考えるとはCPUをできるだけ忙しく働かせて能力を最大限に発揮させる。
- 部品コラボレーション
- 部品の総和は全体よりも大きくなる。
- 小さい部品を集めて大きなソフトウェアを作る
- 90%分解
- 90%の解を目指す。100%を目指すと効率が悪くなるので90%をうまくやれるようにする。
- 劣るが優る
- 劣るほうが優れている。
- UNIXは「最大公約数」的なシステムが生き残ると信じられていて、「高品質で高価」よりも「最高級ではないが効率的」のほうが受け入れられると考える。
- 階層指向
- 構造を階層的に考える。UNIX界では物事を階層的に考えることが好まれる。