凝集度とは

この節の要点

  • 凝集度とはある関数・メソッド・クラスなどのモジュールの処理目的が主だった目的と一致しているか(凝集しているか)を表す尺度である。

結合度と対をなす指標として、凝集度(cohesion) があります。

モジュールの凝集度とは、 モジュールの主目的に対して、どれだけ厳密にサポートしているか、つまりどれだけ処理内容の焦点が主目的に凝集しているかを表します [8][10]。

例えば、java.lang.Mathパッケージに含まれるdouble cos(double a)といったラジアン値に対する余弦関数の値を返すメソッドは、メソッドの処理が1つの機能を実行することに徹しているので、凝集度は高いと言えます。しかし例えば次のようなdouble cosAndTan(double a, boolean flag)といった第二引数の値によって、余弦かあるいは正接関数の値を切り替えて返却するメソッドがあったとすれば、これは先のメソッドと比較しても凝集度は低いと言えます。

以下に、モジュールに対する凝集度の度合いとその分類を段階別に示します[8][11]。ここで、凝集度は高(強)ければ良く、低(弱)ければ悪いとされます。

  1. 暗号的凝集度(最も低い、最も悪い) (Coincidental cohesion)
    • モジュール内の処理同士にそれとわかるような関係性が無い場合
  1. 論理的凝集度 (Logical cohesion)
    • モジュール内の処理同士が1つの目的に関係している場合
    • 例えば、入力処理をまとめたパッケージinputが、キーボードからの入力処理、スキャナからの入力処理、データベースからの入力処理からなる場合、inputは論理的凝集度を持つ
    • 例えば、データ入力メソッドinputAllを仮定する。inputAllはキーボードからの入力、スキャナからの入力、データベースからの入力などすべてから得たデータを文字列出力するメソッドである。このようなinputAllメソッドは論理的凝集度を持つ
  2. 手続的凝集度 (Procedural cohesion)
    • モジュール内の処理が特定の手続き的にあるいはアルゴリズム的な関係性のある処理、すなわちある特定の処理に関係する順接、分岐そして繰り返しのいずれかの処理からなる場合
    • 例えば、読み込んだデータへの消費税処理をまとめたパッケージtaxが、データを読み込む処理、消費税の計算処理、取り出したデータに対して1件づつ消費税の計算処理を適用する処理からなる場合、taxは手続的凝集度を持つ
    • 例えば、読み込んだデータの消費税処理をおこなうcalculateTaxメソッドが、データを読み込み、消費税の計算し、取り出したデータに対して消費税の計算をする処理の場合
  3. 時間的凝集度 (Temporal cohesion)
    • モジュール内の処理がプログラム中の特定のタイミング中で順序が関係なく実行される関係性でまとまっている場合
    • 例えば、ファイル読み込みの初期化処理をまとめたパッケージreadinitが、カウンタ変数を初期化する処理、読み込む配列を初期化する処理、ファイルポインタを開く処理からなる場合、readinitは時間的凝集度を持つ
    • 例えば、ファイル読み込みの初期化処理をまとめたreadInitメソッドを仮定する。このようなreadInitメソッドは時間的凝集度を持つ
  4. 連絡的凝集度 (Communicational cohesion)
    • モジュール内の処理が同じデータを使用するが、データの共有を除いて処理の関連が無い場合
    • 例えば、入力された成績情報に関する処理をまとめたパッケージscore.frominputが、成績表の作成処理、成績データの補正処理、氏名の補正処理からなる場合、score.frominputは(同一の成績情報を利用する点において)連絡的凝集度を持つ
    • あるいは、成績データを作成する処理をまとめたパッケージscore.tooutputが、入力された文字列から成績データを作成する処理、今回の成績と前回の成績から成績データを作成する処理、特定の成績を除外した新しい成績データを作成する処理からなる場合、score.tooutputは(同一種類の出力データである成績データを出力する点において)連絡的凝集度を持つ
  5. 逐次的凝集度 (Sequential cohesion)
    • モジュール内の処理が、受け渡されたデータを加工を施し結果を返却する処理であり、かつ順番に連鎖できる関係性でまとまっている場合。
    • 例えば、成績を表示する処理をまとめたパッケージscoreutilsが、ファイルを読み込み成績データを作成する処理、成績データから補正した成績データを作成する処理、成績データから画面出力を行う処理からなる場合、scoreutilsは逐次的凝集度を持つ
  6. 機能的凝集度(最も高い、最も良い) (Functional cohesion)
    • モジュールがある処理を1つだけ実行する場合

基本的に、我々が作ることになる大体のモジュールは、機能的凝集度となるように心がけるべきです。もちろん、すべてのモジュールを機能的凝集度にすることは困難であることには注意してください。保守性を特に高めたい箇所は選ばなければなりません。このように凝集度を高くするように心がけることで、モジュールが行う処理が明確化されるため、デバッグのしやすさにつながり、保守性が向上します。 例えば、先に述べたcosメソッドやsinメソッドのように、1つのモジュールには 1つの明確な役割 (つまり処理内容、cosメソッドならばラジアン値に対する余弦関数の値を返す、それ以外の処理内容は持たせない)だけを与えるように設計したほうが良いです。

なお、 機能的凝集度を目指すモジュールには当然ながら、良いモジュール名が命名されていなければなりません 。それは、 先に述べた1つの明確な役割は、モジュール名によって(まず初めに )識別されるから です。例えば、void do()関数を見てあなたは何を考えますか。メソッドdoとは一体何をするのだ、大概の人は思うはずですよね。このようなモジュール内の処理が何をするのか全く表現できていない命名は避けましょう。もちろん長すぎても駄目ですし、冗長すぎても駄目です。これはプログラマにとっての悩みの種の1つです。一説には、変数名の最適な長さが9~15文字である[8]から、少なくとも変数名より複雑になりそうにないモジュール名はこれより短くするよう務めましょう。例えば文章を印刷するモジュールなのであれば、printDocumentだし、注文情報が正確か確かめるモジュールであれば、checkOrderInfoでも良いと思います(もちろんこれが最適とは限りません、場合によっては冗長であったり、悪くなる可能性もあります)。 そうはいっても、モジュールの命名は適切に行うこともプログラマにおける重要な関心事であることを覚えておいてください。

[8]: M. Steve, ``CODE COMPLETE 上 完全なプログラミングを目指して'', 第二版, 日経BP社 (2005)

[10]: W.P. Stevens, G.J. Myers, and L.L. Constantine, ``Structured Design'', IBM Systems Journal, Vol.13, pp.115-139 (1974)

[11]: E. Yourdon, L.L. Constantine, ``Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design'', Prentice Hall Inc. (1979)