こんにちは、キャスレーコンサルティング SI(システム・インテグレーション)部の清水(誠)です。
今回は、シンプルかつメンテナンス性の高いコードを書くために重要な概念のひとつである
「モジュール結合度」について取り上げます。
そういえば情報処理試験を受けた時に覚えはしたけど、実務ではあまり意識する機会がなかったなあ……
という方もいらっしゃるのではないでしょうか。
他ならぬ筆者がそうだったので、今回改めておさらいし、具体例と合わせてご紹介してみようと思います。
今後の実務で活用していただたくきっかけとなれば幸いです。
なお、ここで言うモジュールとは関数や変数を一定の単位でまとめたものを指し、
一般的なオブジェクト指向型言語での「クラス」に置き換えていただいても差し支えありません。
また、サンプルコードはC#にて記述します。
モジュール結合度とは?
モジュール結合度とは、モジュール同士の密接さを表す尺度です。
6段階に分類され、結合度が低いほど独立性が高くなり、良いモジュールとされています。
以下、結合度の高い順にご説明します。
1.内容結合
あるモジュールが、別のモジュールの外部宣言していない内部状態を直接参照したり、
命令の一部を共有したりしている状態を指します。
内容結合では、一方のモジュールの変更が他方のモジュールに影響を及ぼすので
障害につながる確率が高くなります。
アセンブラ等の低水準言語では陥りがちですが、高水準言語では一般的には発生しないためサンプルコードは省略します。
2.共通結合
共通域に定義したデータをいくつかのモジュールが直接使用するような状態を指します。
共通域の定義データとはいわゆるグローバル変数、JavaやC#で言うところのstatic変数です。
・共通域のデータは他のモジュールで書き換えられてしまう可能性がある
・共通域のデータ定義の変更がそれを使用しているモジュール全てに影響する
等、デメリットが多くあります。
サンプルコード
using System; public class CommonParameter { // 共通域のデータ public static string Name; public static int Number; public static string Place; } public class CommonCoupling { public string GetMessage(){ var message = CommonParameter.Name + "が" + CommonParameter.Number + "匹います。"; return message; // CommonParameter.Placeは使用しない } } public class TestCommon { public static void Main() { CommonParameter.Name = "犬"; CommonParameter.Number = 2; CommonParameter.Place = "庭"; var data = new CommonCoupling(); Console.WriteLine(data.GetMessage()); } }
実行結果
犬が2匹います。
3.外部結合
外部宣言したデータ(= public static変数)を共有している状態を指します。
データを共有するという点では共通結合と似ていますが、必要なデータのみを外部宣言するため
共通結合よりも結合度は弱くなります。
サンプルコード
using System; public class ExternalCoupling { // 必要なパラメータをstatic化 public static string Name; public static int Number; public string GetMessage(){ var message = Name + "が" + Number + "匹います。"; return message; } } public class TestExternal { public static void Main() { ExternalCoupling.Name = "猫"; ExternalCoupling.Number = 3; var ext = new ExternalCoupling(); Console.WriteLine(ext.GetMessage()); } }
実行結果
猫が3匹います。
4.制御結合
呼び出し側のモジュールが、呼び出されるモジュールの制御を指示するデータ(いわゆるフラグ)を
パラメータとして渡す状態を指します。呼び出し側は相手のモジュールのロジックを知っている必要があるため、
相手をブラックボックス扱いできず結合度が強くなります。
サンプルコード
using System; public class ControlCoupling { public string GetMessage(string name, int number, string type){ string message = string.Empty; // 引数typeによって処理を分岐 if (type == "哺乳類") { message = name + "が" + number + "匹います。"; } else if (type == "鳥類"){ message = name + "が" + number + "羽います。"; } return message; } } public class TestControl { public static void Main() { var ctrl = new ControlCoupling(); Console.WriteLine(ctrl.GetMessage("ハムスター", 4, "哺乳類")); Console.WriteLine(ctrl.GetMessage("ハト", 5, "鳥類")); } }
実行結果
ハムスターが4匹います。
ハトが5羽います。
5.スタンプ結合
共通域にないデータ構造を、2つのモジュール間で受け渡す状態を指します。
結合度は比較的弱いですが、スタンプ結合の場合は受け渡すデータ構造の一部を使用しないことがあります。
サンプルコード
using System; public class StampCoupling { public string GetMessage(StampParameter p){ var message = p.Name + "が" + p.Number + "匹います。"; return message; // p.Placeは使用しない } } public class StampParameter { // 受け渡し用データ(インスタンス変数なので共通域ではない) public string Name; public int Number; public string Place; } public class TestStamp { public static void Main() { var p = new StampParameter() ; p.Name = "アリ"; p.Number = 6; p.Place = "地中"; var stmp = new StampCoupling(); Console.WriteLine(stmp.GetMessage(p)); } }
実行結果
アリが6匹います。
6.データ結合
単純なデータだけをパラメータとして受け渡す状態を指します。
相手モジュールをブラックボックス化できるので、結合度は一番弱くなります。
サンプルコード
using System; public class DataCoupling { public string GetMessage(string name, int number){ var message = name + "が" + number + "匹います。"; return message; } } public class TestData { public static void Main() { var data = new DataCoupling(); Console.WriteLine(data.GetMessage("コツメカワウソ", 7)); } }
実行結果
コツメカワウソが7匹います。
おわりに
一般的に、モジュール同士の結合度が低いほどコードの可読性・再利用性が高まり、
障害が発生した場合も対応がしやすくなります。
プロダクト毎のアーキテクチャやコーディング規約の許す範囲で、
可能な限り結合度を弱く保つコーディングを心掛けましょう。
最後までお読みいただきありがとうございました。