結合の例 - ルーチンの呼び出し

この「いいプログラムを書こう」では、例は、基本的にC言語で書きます。
C言語を直接知らなくても、他のプログラミング言語をご存知なら、簡単にその言語に置き換えできるレベルのコーディングしかしていませんので、ご自分の得意な言語に置き換えてお読みください。
また、C言語を詳しくご存知でない方の読み替えを考慮し、ポインタなどは可能な限り使いませんので、C言語をご存知のかたには物足りないかもしれません(たとえば構造体をそのままスタックに積んでたりしますし(^^;)。あらかじめご了承ください。

単純なデータによる結合の例

foo() {
	double dSeta, dTan;
	...... /* dSetaを設定する */
	dTan = tan(dSeta); /* タンジェントを求める*/
}
このように単純な変数による結合です。
この例ではdSetaをパラメータとしてtanを呼び出します。
最も芸が無い…などと侮っては行けません。
これこそが最も美しく(主観入ってます)、シンプルで、そしてバグが少ない呼び出しかたなのです。

データ構造による結合の例

typedef struct {
	long lEmployeeID;
	char szName[40];
	char szAddress[100];
	long lBirthDay;
	char szPhone[20];
} EMPLOYEETYPE;

foo() {
	EMPLOYEETYPE Emp;

	/* Empに設定 */
	...

	PrintEmployeeData(Emp); /* EmployeeData全体を印刷 */
}
このようにデータ構造を渡す結合です。
これは「まとまった単純データ結合」とみなすことができます。
一般にパラメータの数が8個を超えるとミスをする割合が急激に増えることは本文でも触れましたが、その場合にはこのようにパラメータを構造化するのも一つの回答です。

ところで…

foo2() {
	EMPLOYEETYPE Emp;

	/* Empに設定 */
	...

	PrintEmployeeName(Emp); /* EMPLOYEETYPEのszNameを印字 */
}
こんな場合はどうでしょうか。これもデータ構造による結合をしています。
しかし、その必要があるでしょうか?
よく見てください。印刷するのはszNameだけですよね。
その場合、
foo2_dash() {
	EMPLOYEETYPE Emp;

	/* Empに設定 */
	...

	PrintEmployeeName(Emp.szName);
}
こんなふうに、単純なデータによる結合に昇華(あえて昇華としたい)させましょう。

では、lEmployeeID以外の情報を使うルーチンがあったらどうしましょう?
単純データ結合でパラメータをたくさん並べる。
あるいは、データ構造による結合で、Emp全体を渡し、渡されたほうではlEmployeeID以外を使うようにする。
どちらも間違いではありません。
でも…

typedef struct {
	long lEmployeeID;
	struct tagEMPDATA {
		char szName[40];
		char szAddress[100];
		long lBirthDay;
		char szPhone[20];
	} data;
} EMPLOYEETYPE;

foo3() {
	EMPLOYEETYPE Emp;

	/* Empに設定 */
	...

	PrintEmployeeData(Emp.data);
}
こんなふうに、データからなるサブストラクチャを作成するのがもっともスマートな解決法ですね。
そして、こういったサブストラクチャは意味的にも正しく全体に対する一部分になっているはずなんです。

フラグによる結合の例

foo() {
	DoProcess(1);
	DoProcess(2);
}

DoProcess(int flag) {
	if (flag == 1) {
		/* 月次報告書印刷処理 */
		...
	}
	if (flag == 2) {
		/* 四半期報告書印刷処理 */
		...
	}
	if (flag == 3) {
		/* エラー表示処理 */
		...
	}
	if (flag == 4) {
		/* メール送受信処理 */
		...
	}
}

この結合は望ましくありません

まず名前がよくありません。DoProcess()?なにをするのでしょう?
フラグによる結合を行うと、このようによく解らないネーミングが増えます。
また、「何をする関数」かどうかの定義があいまいになり、グローバル変数を使用する機会が増えます。
ルーチン内で処理対象を切り替えるような作りは、望ましくありません。


グローバル結合の例

typedef struct {
	long lEmployeeID;
	char szName[40];
	char szAddress[100];
	long lBirthDay;
	char szPhone[20];
} EMPLOYEETYPE;

EMPLOYEETYPE EmpTbl[100];

foo () {
	EmpTbl[0].lEmployeeID = 1;
	/* EmployeeID(従業員コード)を設定する…つづく */
	...
	/* EmployeeID(従業員コード)を設定する…ここまで */
	EmpTbl[99].lEmployeeID = 100;

	PrintEmployeeID(50)
}

PrintEmployeeId(int nTableIndex) {

	/* EmpTbl[nTableIndex].lEmployeeID を印刷する...*/
	...

}
この結合は望ましくありません

このような結合はプログラムの可読性、保守性を著しく害します。


ルーチンへ戻る _ Top of Site

Copyright (c) 2000 Takao Tamura