#はじめに
今回『プリンシプル オブ プログラミング』という本を読みましたので、自分なりにアウトプットしていく目的でまとめます。
リンク:『プリンシプル オブ プログラミング 3年目までに身につけたい一生役立つ101の原理原則』
###プログラミングセオリー
プログラミングにおいて、技術だけを習得しても問題解決の際に適切な技術の選択に迷うこととなる。その指針となるのがプログラミングセオリーである。プログラミングセオリーでは下記3つの「価値」が示されている。
- コミュニケーション
- シンプル
- 柔軟性
これらを判断基準とすることで、その判断が正確かつ応用の効くものとなる。また、コードに適用するための「橋渡し」の概念として以下6つの「原則」がある。
- 結果の局所化
- 繰り返しの最小化
- ロジックとデータの一体化
- 対称性
- 変更頻度
- 宣言型の表現
技術を適用する際の考慮すべき観点のことを「フォース」という。その例として下記3つが挙げられる。
- 解決策が満たすべき「要件」(例:プロセス間通信が必須、など)
- 課題に含めれている「制約」(例:プロセス間通信のプロトコルは標準に則る、など)
- 解決策に望まれる「特性」(例:機能の追加が容易、など)
これらのバランスを上手く取り、解決策を見出していくことが肝要である。
言語・ツール・技術・問題領域など、物事の本質や目的を理解した上で作業に当たる必要がある。技術が今の形になった歴史や、何のためにこのような形になったのかについて理解することが、コードの品質に繋がる。何か問題を解決する際には、理由等をきちんと説明出来るまで理解してから、コードを確定するべきである。
##3つの「価値」
###コミュニケーション
コードは書く時間より読む時間のほうが圧倒的に長いものである。コードは広く言えば「コミュニケーションツール」であり、読みやすいコードを書くことで、円滑なコミュニケーションが生まれ、それは円滑な開発に繋がる。コードを書いている時も、読み手の目線に立ってコーディングする必要がある。コンピュータに正しい処理をさせることに意識を囚われず、「人に見せる文書である」ということを頭に置くべきである。
###シンプル
コードをシンプルに保つこと、つまりは、コードが達成しようとする目的に対する複雑さではない「余分な複雑性」をコードから排除することが非常に重要となってくる。そのためには本質的な部分とその他の余計な部分を見極めることが肝要である。それは「他の人」の目線に立ってコーディングすることで、見えてくるものだが、稀にシンプルにしすぎて情報が少なくなりコミュニケーションに支障が出るケースもある。そのようなときは、多少の冗長性を持たせてもコミュニケーションを優先してコードを書くべきである。
###柔軟性
コードは必ず変更されるものであり、柔軟性、つまりはコード変更の容易さを持たせる事は重要なことである。拡張しやすく、且つ、その拡張が他の部分に影響を及ぼさない設計が必要である。しかし、柔軟性を持たせようとすることで、コードが複雑化してしまうことが多い。シンプル且つ柔軟なコードを書くためには、即効果のあるコードのみ書くよう徹底することが肝要である。凝った設計より、シンプルなものからユニットテストなどを経てボトムアップ的に柔軟に対応出来るよう設計するべきである。
##6つの「原則」
以上の3つの「価値」を判断基準として利用していく中で、より具体的な指針として6つの「原則」がある。適宜、自分なりに実際にコード書いてまとめる。(超初心者のコードですので全く模範的なものではありません)
###結果の局所化
一部分の変更を別の部分に波及させないよう「結果の局所化」を行う必要がある。また、影響の波及がどこの部分に起きるのかについて把握できていれば、修正も容易であるが、不明瞭な場合大きなコストがかかる。局所化によって、コード全体を把握しなくても、段階的に必要部分の理解のみでよくなる。
他に、関係性の高いコードに関しては集約してモジュール化するべきである。頻繁に呼び出し合っているモジュールがある場合は統合して、同一機能の密集性を高める必要がある。
int priceA = 100;
int countA = 15;
int priceB = 120;
int countB = 20;
int totalA = priceA * countA;
int totalB = priceB * countB;
...
非常に簡単な例ではあるが、上記のように個別の値を変数に代入しておくことで、数値の変更が波及しない。
###ロジックとデータの一体化
ロジックとロジックが操作するデータは、修正する際たいてい同時に行うため、同じ関数、同じモジュールに置くことを意識するべきである。どのロジックと、どのデータを同じ場所に置くかについては、まず仮の位置に置いたのち、コードを書き動作させる中で妥当な位置を探していくほうが効率的である。
public class Main{
public static void main(String[] args){
int num = args.length;
int[] line1 = new int[num];
int[] line2 = new int[num];
Product p1 = new Product("Product1", 100, 50, 0.1);
int num1, people1= 0;
num1 = num / 3;
num1 = num1 + 50;
people1 = num1;
for (int i = 0; i < num; i += 3){
if (Integer.parseInt(args[i]) < 1){
people1 -= 1;
}
line1[i] = Integer.parseInt(args[i]);
}
LimitedProduct p2 = new LimitedProduct("Product2", 100, 50, 0.1);
int num2, people2= 0;
num2 = num / 3;
num2 = num2 + 50;
people2 = num2;
for (int i = 1; i < num; i += 3){
if (Integer.parseInt(args[i]) < 1){
people2 -= 1;
}
line2[i] = Integer.parseInt(args[i]);
}
}
}
上記のように、インスタンス化及び、値の代入をロジックの直前に置く必要がある。
###繰り返しの最小化
コピーアンドペーストなどでの重複を極力避けてコーディングすべきである。そのためには、コードを小さく分割して管理し、共通項を明確にして、どの程度共通しているのかについて把握する必要がある。これによって、コードの可読性が上がり、修正のコストも削減できる。
public class Main {
public static void main(String[] args) {
int num = args.length;
int[] line1 = new int[num];
int[] line2 = new int[num];
int people1 = method(num);
for (int i = 0; i < num; i += 3){
if (Integer.parseInt(args[i]) < 1){
people1 -= 1;
}
line1[i] = Integer.parseInt(args[i]);
}
int people2 = method(num);
for (int i = 1; i < num; i += 3){
if (Integer.parseInt(args[i]) < 1){
people2 -= 1;
}
line2[i] = Integer.parseInt(args[i]);
}
public static int method(int numM){
int numP = 0;
numP = numM / 3;
numP = numP + 50;
return numP;
}
}
上記のように、同じ処理を行っているところについてまとめることで、同じ処理を追加する際に、繰り返す必要がなくなる。
###対称性
「対処性」つまりは、同じ種類のものは同じレベルで、同じことは同じ表現で表す必要がある。これによりコードの中の類似点が明確になり、コードの可読性が上がる。例として以下を挙げる。
- 「追加」メソッドがあれば、対になる「削除」メソッドを作成する。
- あるグループにある関数は、同じ引数を取るようにする。
- あるモジュール内のデータは、すべて生存期間が同じであるようにする。
- ある関数内で、呼び出す関数の抽象度は同じレベルとする。
...
public Result create(Http.Request rq) {
Form<FormTable> userForm = form.bindFromRequest(rq);
FormTable user = userForm.get();
T_User tuser = new T_User();
tuser.name = user.name;
tuser.birthDay = user.birthDay;
tuser.save();
return ok(views.html.user.render(tuser));
}
public Result edit(Long id) {
Finder<Long, T_User> finder = new Finder<Long, T_User>(T_User.class);
T_User user = finder.byId(id);
return ok(views.html.user.render(user));
}
public Result delete(Long id) {
Finder<Long, T_User> finder = new Finder<Long, T_User>(T_User.class);
T_User user = finder.byId(id);
user.delete();
return redirect(routes.Application.index());
}
}
このように、追加メソッドと同じ抽象度で編集メソッドや削除メソッドを書く。
###変更頻度
モジュールなど1つのものとしてグルーピングされたものに関しては、複数の変更理由を持たせないようコーディングするべきである。変更するタイミングが同じ要素に関しては同じ場所に書き、違うものは別の場所に書く必要がある。これはロジックにもデータにも適用すべき考え方である。
例えば、税額計算での「一般的な計算ロジック」と「年ごとに固有なロジック」は変更タイミングが明らかに異なるため、別のモジュールやグループとする。また、データでは例えば、モジュールのデータで1つの関数実行中のみ使用されるデータがあれば、その関数のローカル変数に移動する。
以上のように、1つのモジュールに複数の役割を持たせず、変更理由を1つにすることで堅牢なコーディングとなる。
###宣言型の表現
コーディングには「命令形」と「宣言型」という2つの方が存在する。「命令形」とはデータの構造やアルゴリズムを記述する型であり、そのアルゴリズム等を、処理の順番に則して並べて記述する。「宣言型」は、ある出力を得る手順ではなく、どのようなデータを必要としているか、問題の定義や性質についてを記述する。JavaやRubyは「命令形」の言語であるが、後者の「宣言型」の性質についても取り入れつつコーディングする必要がある。代表的な手法としてはアノテーションやDSLなどがある。
#アーキテクチャ根底技法
アーキテクチャ根底技法とはよいソフトウェア・アーキテクチャ構築のための基礎原理である。それには10個の技法が存在する。
##【一言まとめ】
- 抽象
- 具体的にではなく、パターン化して本質を捉える。
- カプセル化
- データやロジックは関係のあるものだけでグループ化する。
- 情報隠蔽
- 必要な情報以外は、カプセル化を用いて見せないようにする。
- パッケージ化
- 関係のあるモジュールだけでパッケージ化する。
- 関心の分離
- ソフトウェアの機能や目的によって分離して書く。
- 充足性、完全性、プリミティブ性
- モジュールの内容について、過不足や重複の無いものとなっているか考える。
- ポリシーと実装の分離
- ソフトウェアに依存しているモジュールとしていないモジュールは分離する。
- インターフェースと実装の分離
- クライアントからアクセス可能な部分と、ロジック等の不可能な部分は分離する。
- 参照の一点性
- 変数などに対して再代入を行わない、または、動作を外部と切り離す。
- 分割統治
- 問題に対しては小さく分割して検討する。
ある問題に対し、特定の解決法が他のものよりも優れているということをプログラマたちが認識し、何度も再利用なされてきた解決法のことを根底技法という。これらは開発方法論やプログラミング言語とは関係なく適用される。
##【補足】
###抽象
優れた設計を行うためには「抽象化」することが肝要である。「抽象化」はまとめると「捨象」「一般化」の2つになる。「捨象」とは対象物の本質的な部分のみ残し、他の部分を取り除くことである。「一般化」は複数のものの共通点でくくることである。複雑な対象に取り組む際には、本質的な部分は何か見極めて「捨象」し、複数の対象に取り組むときには適正な共通点を付け出して汎用的な概念を構成する。プログラミングを行うときには、一つ一つ具体的に捉えるのではなく、本質を捉えて「パターン認識」することが必要となってくる。
###カプセル化
関連のあるデータやロジックはグルーピングし、他の要素と混ぜないようコーディングすることが肝要である。これにより、コードの可読性が上がり、変更時の影響の波及が抑えられ、再利用性や、修正のしやすさが向上する。
###情報隠蔽
クライアントに対してはモジュール内の不必要な情報、例えば、関数のロジックや詳細部分を見せないようにし、外部からアクセスできないようにするべきである。これにより、クライアント側から見ても操作しやすくなる。上記のカプセル化によって、この情報隠蔽はスムーズに行うことができる。
オブジェクト指向設計で用いられる、パルナスの規則という概念がある。これは、モジュールの利用者には、そのモジュールを利用するために必要なすべての情報を与え、それ以外の情報は見せない、またモジュールの作成者には、そのモジュールを実装するために必要なすべての情報を与え、それ以外の情報は一切見せない、という条件によって定義されている。つまりは、その人が行うことに必要な情報以外は排除するべきということである。
###パッケージ化
カプセル化によって、コードの整理されたモジュールとなるが、規模が大きくなると、モジュールが大量に書かれることとなる。これを関連性の高いものだけでグルーピングする。これをパッケージ化という。これにより、ソフトウェア全体がパッケージという小さい単位に分割され可読性が上がり、また関連しないモジュールが同パッケージ内に存在しないので管理がしやすくなる。また、修正の影響の波及も抑えられ、依存関係が明確と成るので再利用もしやすくなる。
このようなパッケージ化はボトムアップ的に行うべきであり、ある程度カプセル化によってモジュールができていく中で、パッケージの設計を行い、より良い形でパッケージ化していくのが効率的なやり方である。
###関心の分離
「関心」とは、ソフトウェアの機能や目的のことであり、これらを「分離」して、独立したモジュールを作成する必要がある。これの最も代表的なパターンは「MVC」である。これは「ビジネスロジック」「ユーザーへの表示」「入力処理」を分離している。また、プログラミングにおいて、関心の分離を目的とする代表的な技術は「アスペクト指向プログラミング」である。
「アスペクト指向プログラミング」とは、各機能に散在する横断的な機能に関して(ログ機能など)自動的に、後から、組み込むことで、コードでの直接呼び出しを回避し、分離する技術のことである。
###充足性、完全性、プリミティブ化
モジュールが担う抽象の表現は「充足」し「完全」で「プリミティブ」であるべきである。
-
充足性
モジュールが表現しようとしている抽象がそれを伝えるために十分かどうか。例えば、モジュールがコレクションを表現しているとき「remove」が提供されていても「add」が提供されていなければ、コレクションであることを伝えるには不十分である。 -
完全性
モジュールが表現しようとしている抽象が、すべての特徴を備えているかどうか。例えば、モジュールがコレクションを表現しているとき、その要素数を取得する「size」が提供されていなければ完全とは言えない。 -
プリミティブ性
モジュールで表現しようとしている抽象が全て純粋であるかどうか。例えば、モジュールがコレクションを表現しているとき、アイテムを1つ追加する「add」が提供されていれば、アイテムを10個追加する「add10」は不要である。
以上3つはクライアントに表現している事象を正確に伝えるために必要なことである。そのためにも、そのモジュールがどのような抽象を表現しているのかを明確にする必要がある。
###ポリシーと実装の分離
モジュールでは「ポリシー」または「実装」を扱うが、1つの中で同時に扱ってはいけない。ポリシーモジュールはビジネスロジックやその他のモジュールに対する引数の選択などを行うモジュールであり、実装モジュールはそのソフトウェアの前提に依存しないモジュールのことである。実装モジュールは汎用性、再利用性が高いが、ポリシーモジュールはソフトウェアに依存しているため、ソフトウェアの変更の影響を受ける。そのため、これらが混合していると、実装モジュールの再利用が不可能となってしまう。明確に把握しつつコーディングする必要がある。
###インターフェースと実装の分離
モジュールは「インターフェース」パートと「実装」パートの2つの分離した部分から構成される。「インターフェース」パートでは機能の定義やモジュールの使用方法を定める。これはクライアントからアクセス可能な部分として構成する。「実装」パートでは機能の実現やロジック、データを扱う。クライアントからアクセス出来ないよう構成する。
上記のようにこの2つの部分を分離して実装することで、クライアントへの影響を考えること無く実装の修正を行うことが出来る。また、設計はインターフェースを用いて行う。
###参照の一点性
モジュールの定義について、定義は一回きりとするべきである。変数などに関して、値の再代入を行わず、変数をなるべく不変のものとして設計する必要がある。必要があって残った可変の変数に関しては、可能な限りアクセスするロジックやスコープを極力減らす。小さな関数を多く作成し、その個々の役割を限定することで、不適切な値が代入された場合の原因究明がしやすくなる。
上記のような条件を満たした関数は「参照透過性」がある関数であるといえる。呼び出しの結果が引数にのみ依存するような設計や、呼び出し自体が他の動作に影響を与えない等、動作を外部の状況と分離させて設計することが肝要である。
###分割統治
そのままで解決することが難しい「大きな問題」は、分割して検討することでスムーズに解決できる。例は以下である。
- ソフトウェア全体を設計するときは、独立して設計できる部分に分割してから取り組む
- モジュールを設計するときは「責任・責務」の観点からモジュールを分割する。
- アルゴリズムを設計するときは、マージソートのように、ボトムアップで分割し、問題解決に向けて検討する。
- 大量のデータ処理をするときは、計算を小さい単位で分割し、分散環境で並行して実行できないか検討する。
#アーキテクチャ非機能要件
非機能要件とは機能面以外の全般に対しての要件のことである。
##【一言まとめ】
- 変更容易性
- ソフトウェアは長年に渡って改変されていくので、変更しやすいよう設計する。
- 相互運用性
- 外部の機能やデータ構造にアクセスしやすいよう設計する。
- 効率性
- 時間効率性や資源効率性などと、疎結合にするための間接化のバランスに気をつける。
- 信頼性
- 障害や、不正使用、ミス等が発生した場合にも機能を維持できるよう、状況に合わせて設計する。
- テスト容易性
- アーキテクチャ策定の時点からテストを書くことを考慮に入れた設計を行う。
- 再利用性
- 既存の構造やモジュールに「プラグイン」しやすいソフトウェア、また、再利用しやすい独立したモジュールを作成する。
後回しにされやすい概念であるが、開発の初期段階から考慮されるべきことである。要件定義においては、それぞれの観点について、どの程度必要とされるのかを確認し、開発においては、アーキテクチャ設計の時点で、この要件を考慮に入れた構造を考える。
##非機能要件のテスト
非機能のテストは「どのように動作するか」についてテストを行う。非機能のテストは軽視されがちだが、効能テストと同程度の重要性を持ち、合格基準を具体的に設ける必要がある。また、重要な非機能要件の1つにセキュリティがある。情報のセキュリティの正式な定義は「情報の機密性、完全性、可用性を維持すること」である。
-
機密性の維持
- 第三者に容易に重要な情報にアクセスされないようアクセス制限を実施し、また情報は暗号化し、仮に情報が漏洩しても、この情報を見ることが出来ないようにするという対策を行う。
-
完全性の維持
- 第三者に情報の改ざんや、削除を行われないよう、デジタル署名などの技術を利用する。
-
可用性の維持
- システムトラブルやウイルスによって情報へのアクセスやシステムの利用に支障をきたさないよう、システムや情報を二重化したり、ウイルス対策ソフトでウイルスの感染から守る。
セキュリティの脆弱性を発見するために、ソフトウェアに実際に攻撃を加える検証手法を「ペネトレーションテスト」という。この検証には非常に高度な知識と技術が必要であり、ツールやアウトソーシングを利用して行うべきである。セキュリティを高めようとすると、しばしばユーザビリティに影響が出る。良いバランスを考えて実装していく必要がある。
##【補足】
###変更容易性
ソフトウェアは長く利用されていくものであるため、その変更容易性は運用する上で非常に重要となってくる。そのためにはアーキテクチャの設計において以下の点に留意する必要がある。
- 保守性
- 障害が発生したコードの修正のしやすさのことである。変更の局所化を行うことで実現する。
- 拡張性
- 新しい機能の追加や、新バージョンへの置き換え、不要なモジュールの除去などのしやすさのことである。モジュール間の結合を弱め、独立させることで実現する。
- 再構築
- モジュール間の関係の再組織化を行うことである。モジュールの実装には影響を及ぼさずに、モジュールを柔軟に配置出来るようにする必要がある。
- 移植性
- ソフトウェアを様々なハードウェアプラットフォーム、ユーザーインターフェース、オペレーティングシステム、プログラミング言語、コンパイラに対する適合のしやすさのことである。特定のハードウェアに依存しない設計が求められる。
ソフトウェアのどの部分において高い柔軟性をもたせるかという見極め、シンプルさとのバランスを保つことが、アーキテクチャ設計においてポイントとなってくる。また、ソフトウェアの「経年劣化」についても考慮に入れる必要がある。この原因は以下が挙げられる。
- 柔軟性のない設計によるアーキテクチャの破壊。
- 設計を理解していない人の変更による破壊。
- アーキテクチャが複雑で理解が困難なため、無秩序な変更が行われることによる破壊。
- 更新が行われないことによる陳腐化。
これらを完全に抑制することは困難であるが、「正確なドキュメント化」「変更時に、アーキテクチャを壊さない」「真摯なレビュー」「変更箇所を予測した柔軟な設計」などの対抗手段により、軽減することは出来る。
###相互運用性
ソフトウェアは独立して存在するものではなく、他のシステムや環境に相互作用しているものである。故に、外部の機能やデータ構造へ、アクセスが明確に定義されたアーキテクチャの設計をすることが求められる。また、プロトコルやデータ形式は標準規格を選択する。既にあるソフトウェアに合わせる形になる場合は、接続仕様をよく確認する必要がある。
###効率性
効率性には、時間効率性と資源効率性の2種類ある。時間効率性は一定の時間内に、何件の処理を終えることができるかという「スループット」、ユーザーの入力操作から応答までにかかる時間である「レスポンスタイム」、ユーザーによる作業開始時から求められた情報の出力を終了するまでの時間である「ターンアラウンドタイム」などで計測する。資源効率性は、CPUの使用時間や、メモリ使用量、ストレージ消費量、ネットワーク伝送量などで計測する。また、ただリソースを節約するのではなく、最大限性能を引き出すため適切に利用する必要がある。
モジュール間の直接の結合を避けるため、それらの間に介在する「媒介モジュール」を導入する場合がある。これを「間接化」という。これにより、疎結合性を維持し、高い保守性や拡張性、再利用性を確保することができる。しかし、その分、処理自体は冗長になり効率は下がるため、バランスを考えることが必要となってくる。
###信頼性
信頼性とは例外的な場面、予期しない方法、不正な方法で使用された場面に置いても、機能を維持する能力のことである。これには2つの側面がある。
-
フォールトトレランス
ソフトウェアに障害が発生した時に、正常な動作を保ち続ける能力である。内部的には修復を行う。例外を回復した後、例外が発生した位置の操作の実行を再開したり、繰り返したりする必要がある。 -
ロバストネス
不正な使用方法や入力ミスから、ソフトウェアを保護する能力である。例外処理を起こした処理については、内部的な修復は必ずしも要求しない。ソフトウェアを定義された状態に移行することを保証する。
大規模なシステムか、個人用のソフトウェアであるかによっても、求められる機能維持のレベルは様々ある。それぞれあらかじめ明確にして、それに沿ったアーキテクチャ設計を行う必要がある。また、フォールトトレランス観点の対策として、アーキテクチャに内部的な冗長性をもたせるようにすることなどがある。また、障害時は、大事な機能のみの提供に絞り処理継続を優先する設計(フェールソフト)も考慮する。ロバストネス観点の対策としては、障害時にその部分を切り離す設計(フェールセーフ)を検討する。また、そもそも、障害が発生しないよう、あらかじめユーザーが誤った操作を行っても安全に稼働させる設計(フールプルーフ)も考慮する。
###テスト容易性
ソフトウェアは規模が大きくなり複雑化するに従ってテストを書くことがより困難なものになっていく。開発でも保守でも「検証」が容易であることは非常に重要なことである。アーキテクチャ策定の時点からテストを書くことを考慮に入れた設計を行うことが必要である。これにはモジュール間の依存関係の排除がポイントになる。極力依存関係を排除し、小さい単位でテストが可能となるように設計する必要がある。
###再利用性
再利用性とは、ソフトウェアを別のソフトウェアの開発に再利用する能力のことである。これには2つの側面がある。
-
再利用するソフトウェア開発
プロジェクト内の既存モジュールや各種ライブラリなどを利用することを意味する。再利用可能な成果物を、開発中のソフトウェアに統合する。 -
再利用のためのソフトウェア開発
将来のプロジェクトで再利用できるようなモジュールを、現在のソフトウェア開発で作り出すことを意味する。
再利用するソフトウェア開発の場合、アーキテクチャの構成を、既存の構造やモジュールに「プラグイン」できるようにする。これは、ソフトウェアを既存モジュールから組み立てる「ソフトウェア・コンポジション」を支援する目的がある。一方、再利用のためのソフトウェア開発をする場合は開発中のソフトウェアから自己充足的な部分を取り出すことができるアーキテクチャにする。このような部分に関しては、他のシステムで再利用できること、独立してビルドできるモジュール、ないしパッケージであることが求められる。
##7つの設計原理
この原理は、コードレビューの時にチェック観点として使用する。以下7つの観点がある。
- 単純原理
- 同型原理
- 対象原理
- 階層原理
- 透明原理
- 明証原理
- 安全原理
- 線形原理
ソフトウェアの品質担保の手法として、コードレビューは有効であるが、一定の価値観や観点がないと、指摘が散漫になる。上記の観点を以て行うことが必要である。また、コードを書く際もこれらを考慮に入れて行うべきである。
###単純原理
バグはコードの複雑なところで発生することが多い。そのため、高度なコーディングを行おうとせず、単純に小さくコーディングする必要がある。
###同型原理
同じ処理に関しては、同じ形で書くことを同型原理という。これにより、異なっている部分が明確になり、バグを見つけやすくなる。統一感のあるコードは、可読性も高くなる。一貫性のあるコーディングを意識する必要がある。
###対称原理
ある処理について「対」になるものを考慮することが必要である。「条件」があれば「反条件」も考え、整合性を保って検討する。また、例外的な状況も考慮しつつそれをなるべく排除するべきである。他に、命名にも「set/get」「start/stop」など、一般的対称性を持たせる必要がある。
###階層原理
「構造が階層であることにこだわる」原理のことを階層原理という。明確に階層構造をとって設計をすると、可読性が向上する。また、同階層の抽象レベルを合わせることでよりわかりやすいコードとなる。
###線形管理
「処理のながれは直線であることにこだわる」原理のことを線形管理という。そのためには、処理の分岐を少なくする必要がある。処理の一貫性やルートにこだわって、コードを俯瞰し、複雑になっていないか、確認しつつ設計する。複雑になってしまった場合は再構築することも視野に入れ、明確で堅牢なコーディングを意識する。
###明証原理
「ロジックの明証性にこだわる」原理のことを明証原理という。明証とはロジックが明確であるということである。コードを読み、即座に理解できない部分は排除するか、コメントを付けるかの対策を取る。また、変数等の命名もどのような役割のものなのか明確に分かるように行う。
コードを再利用する場合には、そのロジックや仕組みを確実に理解してから使用するようにする。また、既存のコードの修正を行う際は不具合の原因やどのように不具合が起こっているかについてきちんと把握してから、改修する必要がある。
###安全原理
「安全性にこだわる」原理のことを安全原理という。これはありえないだろうと思われる条件でもあえて考慮して、安全性のあるコードを書くよう意識する必要がある。例えば、if分に対してelse文を考慮したり、case文に対し、default文を考慮したり、NULLチェックを行うなど、ありえないと考えられるようなところも安全性にこだわって実装する。
要件や機能の仕様書は必要要件に過ぎない。十分条件を満たすコードを書くには、安全性を高めて、必要条件を満たしつつ、機能と状況を鑑みて堅牢な実装を行う必要がある。
##参考文献
『プリンシプル オブ プログラミング 3年目までに身につけたい一生役立つ101の原理原則』上田勲 著 株式会社秀和システム 2016年3月29日 p.64-128