🦧

設計原則について ~凝集度~

2023/06/22に公開

はじめに

初めまして、ポートのバックエンドを担当している @nakahara です。

今年も早いもので、もう6月になってしまいました。
弊社では例年、新卒入社のエンジニア向けの技術研修をこの時期に行っています。

研修内容は、バックエンド、フロントエンド、インフラ、アプリと多岐に渡るのですが、
今回はバックエンドの研修内容の一部を抜粋して、設計原則に関する記事を作成しようと思います。

設計原則とは

「オブジェクト指向に基づく設計の考え方の指針」という捉え方をしています。
問題に対して適用した構造が、その解決として良い設計になっているかを確かめるために用いる一定の基準といったところでしょうか。

重視している項目

弊社の研修では、下記3項目に重きを置いて説明しています。

  • 単一責任原則
  • 開放閉鎖原則
  • 凝集度と結合度

今回は凝集度に的を絞って、解説していこうと思います。

凝集度とは

凝集度とは、プログラム内の1つのモジュールがどれだけ単一の役割に集中できているかを表す概念です。

凝集度が高いモジュールは1つの役割に集中しており、モジュール内のコードは論理的に関連しています。
逆に凝集度が低いモジュールは、複数の役割に関するコードが混在しており、一つの役割に修正を加えた場合に意図しない他の役割にも影響出る可能性がある、メンテナンスが困難なモジュールとなってしまいます。

つまり凝集度を高く保てば保つほど、可読性、保守性の高いコードを実現できることになります。

凝集度の7つのレベル

凝集度を測る指標として7つのレベルが定義されています。
具体例を交えて、凝集度が低い方から順に紹介していきます。

😩 偶発的凝集(Coincidental Cohesion)

無作為に集められた処理がまとまっているモジュール。
モジュール内の各処理に特に関連性がなく、凝集度が最も低い状態。

coincidental_cohesion.rb
def method
  user = User.find
  p 'Hello world!'
  event_create
end

🤔 論理的凝集(Logical Cohesion)

論理的に似た処理をまとめたモジュール。
共通化などを目的に作成されることがあるが、結果として保守性が低くなることが多い。
可能な限り避けたい状態。

logical_cohesion.rb
def animal_sounds(animal)
  case animal
  when Dog
    'ワンワン'
  when Cat
    'ニャーニャー'
  end
end

🙂 時間的凝集(Temporal Cohesion)

同じタイミングで実行される処理をまとめたモジュール。
中身の実行順序を入れ替えても動作する。

temporal_cohesion.rb
def initialize_for_app
  events = Event.all
  articles = Article.all
  users = User.all
end

🙂 手順的凝集(Procedural Cohesion)

ある種の処理を行うときに動作する部分をまとめたモジュール。
処理の順番に意味がある場合が多い。

procedural_cohesion.rb
def update_client_profile(client_id, profile_attributes)
  if current_user.admin?
    client = Client.find(client_id)
    client.profile.update(profile_attributes)
  else
    raise ActionController::MethodNotAllowed
  end
end

🙂 通信的凝集(Communicational Cohesion)

同じデータを扱う処理をまとめたモジュール。
処理の順番に意味は持っていない場合が多い。

communicational_cohesion.rb
def send_mails(data)
  send_mail_to_user(data)
  send_mail_to_client(data)
end

🙂 逐次的凝集(Sequential Cohesion)

ある部分の出力が、次の出力となるような処理をまとめたモジュール。

sequential_cohesion.rb
def create_rate_users_participated(event_id)
  event = Event.find(event_id)
  rate = calculate_rate_users_participated_in(event)
  save_rate_users_participated(rate)
end

😊 機能的凝集(Functional Cohesion)

単一の役割を持つモジュール。
凝集度が最も高い状態。

functional_cohesion.rb
def right_triangle?(height, width, hypotenuse)
  Math.hypot(height, width) == hypotenuse
end

まとめ

今回、紹介した指標に従って凝集度を高くすることが、プロジェクト全体の保守性や拡張性を保つことに繋がっていきます。
しかし、すべてのモジュールを機能的凝集にすることは不可能です。
凝集度が低いモジュールを可能な限り作らないということが大切になります。

また、凝集度を高めるにはモジュールを細かく分けてあげる必要があります。
一方で、モジュールが増えれば増えるほど、認知コストが上がってしまうという側面もあることを忘れてはいけません。
メンテナンス性と凝集度の落とし所をうまく見つけることが、良いコードを生み出すコツなのかもしれません。

Discussion