🍦

知っておくとためになる。モジュール凝集度について解説

2024/11/24に公開

はじめに

良いコードを書くことは、ビジネス面だけでなく、生産性の向上という点でも非常に重要です。『良いコード』を書くためには業務理解やデザインパターン、情報工学の理解など様々な知識が要求されます。そんな中、良いコードを書くために理解しておきたい要素の一つがモジュール凝集度だと考えています。
本記事では、モジュール凝集度とは何か、その重要性、そして知っておくべき異なる凝集度のレベルについて解説していきます。

モジュール凝集度とは?

モジュール凝集度とは、モジュールやコンポーネント内の要素がどれだけ関連し、単一で明確な目的に集中しているかを示す指標です。
高い凝集度を持つモジュールは、単一で明確に定義された責任を持っているため、理解しやすく、保守や再利用が容易です。一方、凝集度が低いモジュールは、関連性のない複数の責任を持つことが多く、混乱や複雑性の増加、バグの原因となる可能性があります。1つの機能が1つの場所に固まっているモジュールが良いとされています。

なぜモジュール凝集度が重要なのか?

高いモジュール凝集度を維持することは、以下の理由から重要です:

  1. 理解しやすさ: 凝集度の高いモジュールは、明確で集中した目的を持つため、理解が容易になります。これにより、開発者がコードを理解し、その動作を推論しやすくなります。
  2. 保守性: 高凝集度のモジュールは保守性が高く、変更がモジュール内に限定されるため、システムの他の部分に予期せぬ副作用を引き起こすリスクが減少します。
  3. 再利用性: 凝集度の高いモジュールは、明確に定義された責任を持ち、他のコンポーネントと緊密に結合していないため、異なるコンテキストで再利用しやすくなります。
  4. テストの容易さ: 凝集度の高いモジュールは、責任が明確なため、テストケースを特定しやすく、テストの分離が容易です。

モジュール凝集度の7つのレベル

モジュール凝集度は、最も望ましくないレベル(低凝集度)から最も望ましいレベル(高凝集度)まで、7つのレベルに分類されます。

1. 偶発的凝集度 : Coincidental Cohesion(Bad)

  • モジュール内の要素が意味のある関連性や共通の目的を持たない状態。
  • とりあえず一つのモジュールにまとまっているだけ。
# これらの関数には論理的な関連性がなく、同じモジュールに含めるべきではありません

def main(radius, recipient, text):
    # 面積の計算
    calculate_area(radius)
    
    # メールを送信
    send_email(recipient)
    
    # 回文か確認
    is_palindrome(text) 

2. 論理的凝集度 : Logical Cohesion (Not Good

  • モジュール内の要素が論理や機能によって関連しているが、単一の明確な目的を持たない状態。
  • 特定のフラグによって処理を分岐させるモジュール。
# これらの関数は面積に関連していますが、単一の明確な目的を持っていません

def calculate_area(shape, size):
    # 正方形の面積
    if shape.lower() == "square":
        return size ** 2
    # 円の面積
    elif shape.lower() == "circle":
        return math.pi * size ** 2
    # エラー
    else:
        raise ValueError(f"Unsupported shape: {shape}")

3. 時間的凝集度 : Temporal CohesionNot Good

  • モジュール内の要素が特定の時間によって関連している状態。
  • 機能間に強い関連性はなく、特定の時点にて連続的に処理が実行されているだけ。
# これらの関数は実行される順序によって関連していますが、単一の明確な目的を持っていません

def initialize_application():
    # データベースに接続する
    connect_to_database()
    
    # 設定ファイルを読み込む
    load_configuration()
    
    # ログサービスを開始する
    start_logging_service()
    
    # キャッシュを初期化する
    initialize_cache()
    
    # ユーザーインターフェースを起動する
    launch_user_interface()

4. 手続き的凝集度 : Procedural CohesionAcceptable

  • モジュール内の要素が特定のタスクを達成するため実行する順序によって関連している状態。
  • 時間的凝集度の特性を持つが、手順的な関連性があるため時間的凝集度より強度は高い。
# これらの関数はユーザー入力を処理する手続きによって関連していますが、まだ複数の責任を持っています

def write_file(data):
    # ファイルを開く
    open_file()
    
    # ファイルにデータを書き込む
    wirte_file(data)

5. 通信的凝集度 : Communicational CohesionAcceptable

  • モジュール内の要素が操作するデータや通信するデータによって関連している状態。
  • 同じデータを扱う処理がまとまっている。
# これらの関数はユーザーデータを処理する手続きによって関連していますが、まだ複数の責任を持っています

def update_user_data(user_data):
    # 権限更新
    update_user_admin(user_data)
    
    # 名前更新
    update_user_name(user_data)

6. 順序的凝集度 : Sequential CohesionAcceptable

  • モジュール内の要素が特定の順序で実行されることによって関連付けられている状態。
  • 特定の順序において、処理Aの出力結果が次の処理(処理B)の入力値となっている場合、順序的凝集度を持つとされる。
# これらの関数は登録メールを送信する操作の順序によって関連しており、明確で集中した目的を持っています

def handle_registration_email(email):
    # メールアドレスを検証する
    if not email or "@" not in email:
        # 無効なメールアドレスを処理する
        print("無効なメールアドレスです。")
        return
    
    # 登録メールを送信する
    print(f"{email} に登録メールを送信しました。")

7. 機能的凝集度 : Functional CohesionGood

  • モジュール内の要素が単一の明確に定義された機能や責任によって関連している状態。
  • この凝集度は最も望ましく、理解しやすさ、保守性、再利用性を促進する。
# この関数は単一の明確に定義された責任を持っています:円の面積を計算すること
def calculate_circle_area(radius):
    return math.pi * radius ** 2

まとめ

モジュール凝集度は、ソフトウェア開発において非常に重要な側面であり、コードの保守性、理解しやすさ、再利用性に直接影響を与えます。特に機能的凝集度を目指して高い凝集度を維持することで、よりクリーンで保守性が高く、理解しやすいコードを作成することができます。

各モジュールに単一で明確に定義された責任を持たせ、関連性のない機能や責任を混在させないように努めることで、チームメンバーにとっても扱いやすいコードベースを実現してきたいです。

参考文献

Software Engineering | Coupling and Cohesion - javatpoint

Coupling and Cohesion

良いコードとは何か - エンジニア新卒研修 スライド公開|CyberZ Developer

プリンシプル オブ プログラミング3年目までに身につけたい一生役立つ101の原理原則

Discussion