プログラムを作成するにあたっては、ロジックの誤りを防ぐようにすること、保守・改訂が容易にできるようにすることが大切です。それには、自分にも他人にもわかりやすいプログラムにする必要があります。
わかりやすくする手段の一つとして、プログラムを個々の機能に分割して(モジュール分割して)、そのモジュールを構造的に整理するのが効果的です。
ここでは、モジュール分割の方法とその評価基準であるモジュールの強度と結合度について理解します。
モジュールとは、部品のことです。コンポーネントということもあります。一連の機能を行う複数のプログラム群や一つのプログラムを指すこともありますし、プログラムを細分化したサブプログラム(サブルーチン、関数)あるいはプログラム内での処理ブロックを指すこともあります。
- 構造化設計
- 構造化設計(構造化プログラミング)とは、プログラムを作成するとき、処理の内容をモジュールに分割して、階層的な構造となるようにプログラミングすることです。
プログラムの構造が明確になるため、
自分にも他人にもわかりやすいプログラムになる
そのため、プログラムの誤りが発生することが少なくなる
重複する処理を見つけてまとめるなど、無駄の少ないプログラムになる
などの利点があります。
実際にプログラミングするときには、連続(シーケンス),選択,繰り返しの3つの基本制御構造を用い、GO TO文は使うべきでないとされています。
モジュール分割の方法として、
処理の流れに着眼した方法
STS分割(源泉、変換、吸収)、TR分割(トランザクション分割)など
処理の構造に着眼した方法
ジャクソン法、ワーニエ法など
共通の機能に着眼した方法
があります。
- 分割統治法
- 大規模な問題を効率的に解く手法です。全体をいくつかの小さな問題に分割して、それぞれの小さな問題を独立に処理した結果をつなぎ合わせて、最終的に元の問題を解決する方法です。
この小さな問題をモジュールと考えれば、構造化プログラミングとは、分割統治法を体現したものだといえます。
また、分割統治法は、再帰呼び出しと組み合わせて簡素な論理構造や短いプログラムで大きな問題を効率よく解くことができます。クイックソートや動的計画法などで用いられています。
- モジュール粒度
- モジュールが持つ機能の大きさを粒度といいます。階層を深くしていくに伴い、粒度は小さくなり、極端には一つの命令にまでなってしまいます。逆に、機能を大きくして、請求書出力処理というレベルを一つのモジュールとすることもできますし、極端には売上処理というモジュールを考えることもできます。
粒度を小さくすると、
モジュールの内容が簡潔になる
部品として再利用できる機会が増大する
などのメリットがある反面、
階層が深くなり、全体を把握しにくくなる
モジュールの個数が多くなり、覚えられないし、管理ができなくなる
部品として使うことのメリットが少なくなる
などのデメリットがあります。
粒度を大きくすると、この逆のメリット・デメリットになります。
どの程度の粒度が適切であるかは、状況により異なりますが、あまりにも異なる粒度のモジュールが混在すると、管理がしにくくなります。
- 関数(function)
- モジュールを実際にプログラムにしたものを関数あるいはサブルーチンといいます。
funciton 関数名(引数1, 引数2, ・・・)
{ この関数での処理内容 }
の形式になっています(プログラミング言語により若干異なりますが)。
例えば、
function add(x,y)
{ add = x + y; };
という関数addを定義して、それを呼び出すプログラムで
z = add(1, 2);
あるいは、
a = 1; b = 2;
z = add(a, b);
とすると、zの値 3 が戻されます。
この場合、xやyを仮引数、aやbを実引数といいます。
モジュール分割の評価基準
モジュール分割の方法は多様ですが、何らかの都合によりモジュールを変更しても、他のモジュールに与える影響が少ない(変更する必要がない)こと、そのモジュールを部品として再利用できるようにすることが望まれます。すなわち、モジュールの独立性が高いのが優れたモジュール分割だといえます。その評価基準にモジュール強度とモジュール結合度があります。
モジュール強度(凝集度、結束性ともいう)は、モジュール内の機能間の関連性の強さを表す概念です。モジュール強度が強いほど独立性が高くなります。
モジュール結合度とは、モジュール間の結合の度合いを表す概念です。モジュールAとモジュールBが、共通のデータXを使用する場合、あるいは、モジュールAがモジュールBにデータを受け渡すときの方法が結合度に大きく関係します。
モジュールの結合度が弱いほど独立性が高くなります。
すなわち、モジュール強度を強く、モジュール結合度を弱くなる境界で分割するのが、優れた分割方法なのです。
モジュール強度 モジュール結合度
強度 結合度
優 強 弱
↑ ↑機能的強度 ↑データ結合
| |情報的強度 |スタンプ結合
| |連絡的強度 |制御結合
| |手順的強度 |外部結合
| |時間的強度 |共通結合
| |論理的強度 |内容結合
↓ ↓暗号的強度 ↓
劣 弱 強
モジュール強度
- 暗号的強度
- この「暗号化」はセキュリティとは無関係で「分割の意図が他人には理解しにくい」という意味です。
たいした理由もなく、プログラムの途中で分割したので、
モジュールのなかに、関連性のない複数の機能が含まれている
このモジュールだけでは、特定の機能が処理できない
すなわち、このモジュールは何をするモジュールなのか定義ができない
ようなモジュールです。
このような分割を行うと、かえってわかりくいプログラムになってしまいます。
- 論理的強度
- 論理的に関連のある複数の機能を1つのモジュールにまとめており、どの機能が実行されるかは、呼び出されるときの引数の値によって決定されるモジュールです。
プログラムななかで、Aの個所とBの個所に重複する部分が多いので、それを一つのモジュール(関数)とし、AとBのの機能を使い分けるために、
function AORB(A)
のようにして用いるモジュールです。
- 時間的強度
- 例えば、あるファイルをオープンする、ある集計のための変数の値を0とするというような、複数の機能のそれぞれに必要な操作を、初期設定の時点で一括して実行するので、一つのモジュールにまとめたというように、特定の時点で行う複数の機能をまとめたモジュールです。
プログラムを理解するためには有効ですが、無関係のものが一つのモジュールに押し込まれているので、ロジックの理解には役立ちませんし、他のプログラムで再利用することもできません。
- 手順的強度
- 時間的強度とは逆に、逐次的に行う複数の機能をまとめて、実行するようにしたモジュールです。
例えば、二つの機能A、BはA→Bの順で実行されるので、一つのモジュールにまとめたようなときに作成されます。
時間的強度と同じような評価になります。
- 連絡的強度
- 手順的強度に加えて、Aで処理したデータがBで参照されるとか、AとBが同じデータを参照するというように、モジュールの要素間で同じデータの受け渡しや参照が行われるモジュールです。
手順的強度よりもモジュール化した意味がわかります。
- 情報的強度
- 特定のデータを扱う複数の機能(例えば入荷したので在庫の値を増やす、出荷したので減らすなど)を一つのモジュールにまとめたものです。
データとデータの操作を独立のものとして取り扱うことが可能なので、サブシステムの独立性を高めることができます。
- 機能的強度
- モジュール内のすべての機能が単一機能を実行するために関連しているモジュールです。
例えば、データベースを扱う機能をデータとともに一つにまとめ、データベースの内容ををモジュールの外から見えないようにする(オブジェクト指向での隠蔽)ことなどがこれにあたります。
情報的強度では、情報(データ)が中心になっていますが、機能的強度では機能が中心になっており、抽象性が高くなるので、独立性も高まります。
モジュール結合度
変数XをモジュールA、モジュールBの双方が利用する場合、次の方法があります。
変数Xを共通域にグローバル変数(大域変数、外部変数)として定義しておく。
モジュールAがモジュールBを呼び出すときに、変数Xを引数として指定する。
引数として受け渡すほうが、結合度が弱く(独立性が高く)なります。
その変数Xの構造に関しては、次の2つがあります。
変数Xは単一のデータである。
複数の変数をまとめた構造体とする。
多数の変数を列挙するのは面倒である
ファイルのレコードのように、当初から構造体になっている変数もある
構造体では構造が固定されてしまうので、結合度は強くなります。
すなわち、単一データの変数を引数として持ち、それ以外にはモジュール間でデータの受け渡しがないようにモジュール分割するのがよいのです。
- 内容結合(内部結合)
- 内容結合とは、グローバル変数や引数などの考慮をせず、勝手に変数にアクセスする内容を持つ結合です。
モジュールA内部のデータXをモジュールBが直接アクセスするように、他のモジュールの内容の一部を共有します。
エラーがあったとき、Xの状況がモジュールBで変化するので、モジュールAだけを調べても解決できません。
モジュールAを変更すると、モジュールBも変更する必要があります。
- 共通結合
- 構造体を共通域にグローバル変数として宣言し、それを参照します。
グローバル変数方式であること、構造体であることから、結合度の強い結合になります。
- 外部結合
- 単一データの変数を共通域にグローバル変数として宣言し、それを参照します。
必要なデータだけを外部宣言して共有するので、共通結合よりも結合度が弱くなります。
- 制御結合
- モジュールAがモジュールBを呼び出すときに、モジュールBの実行順序などの制御情報を、引数として与えるものです。
引数方式なのですが、モジュールBの実行に大きな影響を与えるので、結合度が強くなります。
- スタンプ結合
- データ構造(構造体、レコード)を引数として受け渡します。
- データ結合
- 単一データの変数を引数として受け渡します。
前述のように、最も結合度の弱い結合になります。
他のモジュールを意識せずに変更することが容易になりますし、モジュールを再利用するにも適したものになります。