読書要点「プリンシプルオブプログラミング」第4章


第4章 視点

プログラミングの判断基準となりうる視点、見方である。
プログラミングは選択の連続だが、効率的に将来を見据えた開発を行う際の判断材料となりうる。

凝集度

モジュールに含まれる機能の純粋さを表す指標として、凝集度がある。
凝集度には7つのレベルがあり、低いレベルのものほど望ましくない。
凝集度が高いほど、設計やコードが見通しやすく、保守性や再利用性が向上される。
また疎結合性も高まり、そもそも他のモジュールの影響も受けにくくなる。

  • レベル1:暗合的強度

モジュール内の要素に関連性が認められない状態。
例えば、たまたま重複していた機能をひとまとめにしたような場合が考えられる。
モジュールの機能が完結していないことが多く、他モジュールとの結びつきが強い可能性も高く、保守性・再利用性が低い。

  • レベル2:論理的強度

モジュールの機能を抽象的に捉えてまとめた状態。
例えばデータの入出力、データベースのCRUD操作などの機能群をひとまとめにしたものがあげられる。
このモジュールが呼び出されたときに実行される命令は一部だけなので、機能ごとの関連性は弱くモジュール強度は弱い。

一方で良い面もあり、関連した機能の一部の論理を共有できること、プログラマがコードを把握しやすいこと、情報隠蔽がある程度できていることがあげられる。

  • レベル3:時間的強度

特定の時間に連続して実行する複数の機能を1つにまとめた状態。
機能間の関連性は考えられていないが、初期処理のように同時に実行される機能がひとまとめにされている。

  • レベル4:手順的強度

時間的強度に加えて、問題の処理に必要な一連の機能のうち一部をひとまとめにした状態。
時間的強度に手順的な関連性が認められ、強度が増している。
手順的強度のモジュールは、モジュール内の機能を取り出すことができない。
ただし、機能を取り出すための機能選択を行うと論理的強度となり、強度を落とすことになる。

  • レベル5:連絡的強度

手順的強度に加え、機能の間でデータの受け渡し、同じデータの参照を行っており、データに関して繋がっている状態。
データによる結びつきの分、手順的強度より強度が増している。

  • レベル6:情報的強度

特定のデータ構造を扱う機能を1つにまとめた状態。
データの修正時の影響をそのモジュールに限定でき、データ構造内部の情報隠蔽にもつながる。
論理的強度の問題点であったデータの扱いにくさが解消されている。

  • レベル7:機能的強度

モジュール内のすべての命令が一つの役割を果たすために関連しあっている状態。
機能を呼び出すときはそのモジュールを呼び出すだけで完結しているので、モジュールの独立性が非常に高い。

結合度

モジュール間の関係の密接さを表す尺度。
レベルが高いほど関係が薄く疎結合である。
モジュールは独立であるほど安定になるため、結合の度合いが小さいものを作りたい。
できるだけ変数を用いたやり取りを行い、変数を引き渡す際も無駄な変数が生まれないようにしたい。

  • レベル1:内容結合

モジュールの内容を共有している状態。
一方の変更が他方に影響を与えることが多い。

  • レベル2:共通結合

共通域のデータを、複数のモジュールが共同で使用している状態。
データの変更が他モジュールに影響を与えてしまいがちで、可読性、再利用性、安全性が低い。
データを扱うモジュールの設計をしっかり行い、共通結合を採用しなくて済むような設計を行いたい。

  • レベル3:外部結合

外部宣言されたデータを共有している状態。
データを共有しているが、必要なデータだけを宣言しているので、その保証されている分結合度は弱くなる。

  • レベル4:制御結合

呼び出し側が、呼び出されるモジュールの制御に必要なパラメータを渡している状態。
呼び出し側が呼び出される側の論理を知っている必要があり、ブラックボックス化できない。
また、呼び出される側のモジュールが論理的強度になってしまう。
ただ、データの共有を行っているわけではないのでモジュール間の結合度は弱くなる。

  • レベル5:スタンプ結合

共通域にないデータ構造を、パラメータを介して受け渡している状態。
不必要なデータを受け渡すことがある点が結合度を強めている。

  • レベル6:データ結合

モジュール間のインターフェースで受け渡されるデータがスカラ型のものだけである状態。
相手モジュールのブラックボックス化ができる上、強度的にも機能的強度となる。
また、スタンプ結合で渡されるデータ構造のうち、全てが使用されている場合はデータ結合とみなしても良い。

直交性

直交性(Orthogonality)は、独立性と分離性に分けて考えることができる。
片方の変更がもう片方に影響を与えない場合、そのコードは直交していると言える。
直交しているコードはを記述していくと、変更の局所化により生産性が向上し、粗結合性によりコードの再利用が高まる。
また、依存性が低いため、堅牢なコードになりテストも容易になる。

コードの結合度を下げると直交性のあるコードの記述につながる。
そのためには、レイヤー化を行いモジュール間の関係を整理しながら結合度を小さくしていくと良い。

可逆性

可逆とは、ある条件を加えると元の状態に戻る性質である。
プログラミングにおける判断では、常に可逆性を持つようにする。

内部要因、外部要因のどちらにかかわらず、今まで行ってきた方針を転換することになるケースは頻繁にある。
やり直しがきかない場合の被害は大きくなってしまう。

過度に特定の技術に依存せず、やり直しがきくコードを書くようにしたい。

コードの臭い

わかりにくい、修正しにくい、拡張しにくいと感じられる部分は、不吉な臭いを放っていると言える。
そのようなコードはリファクタリングの対象となる。

リファクタリングを行うためには、リファクタリングの対象を見極める能力を身につける必要がある。
よく見るコード、長すぎるコード、大きすぎるコード、多すぎるモジュール、名前が合わないコードは臭いを放っているかもしれない。

技術的負債

プログラミングには、素早く汚いコードを書くという選択肢と時間をかけてきれいなコードを書くという選択肢がある。
時間が十分に確保されているのであれば後者を選択すべきだが、やむを得ずに前者を選択する場合もある。
前者を選択した場合にそのコードは、技術的負債を抱えることになる。
技術的負債とは修正しにくい、理解しにくい、問題を抱えたコードの汚い部分である。
今は問題なくとも後々になってその問題が露呈する可能性が高い。
また、技術的負債を返済できなかった場合には利子が発生する。
負債を返済しないまま変更を加えると、コードは更に乱雑になっていき返済することができなくなってしまう。
最終的には保守も機能追加もままならないコードになる。

負債の発生はある意味必然であり、負債を発生させないこと以上に負債とうまく付き合うことが重要である。
負債を発生させざるを得ない場合は、負債の完済まで見通した計画を立てておくのが良い。
また、負債を作らないに越したことはないため、可能な限りきれいなコードを書くように努めたい。