こんにちは。
基本情報技術者試験の範囲にもある「モジュール強度」ってこういうものなのかと整理できたので、記事にしたいと思います。
※Railsをメインに勉強をしているため、扱うコード例はRubyやRailsをベースにしています。
記事の内容に誤りがありましたら、コメントでご指摘いただけますと幸いです。
モジュール
まず、「モジュール強度」の「モジュール」ってなんのことでしょう。
Rubyの勉強をしていると、「モジュール」という用語がでてきます。
こんな風に定義するものです。
module モジュール名
# モジュールの定義(メソッドや定数)
end
一方で、「モジュール強度」がいう「モジュール」は、もう少し広い意味をもっていて、
クラスやメソッドなど、ひとまとまりの機能単位(部品) のことをいいます。
モジュール強度
基本情報の参考書をみると、モジュール強度の説明はこんな感じです。
- モジュール内の構成要素間の関連性の強さを示すもの
- 強度は7種類あり、強度が強いほど、モジュールの独立性は高くなる
(強度は、弱い順で以下のとおり)
| 強度 | 説明 | 特徴 |
|---|---|---|
| 暗号的強度 | 無関係な処理の寄せ集め | 全く関連性がない |
| 論理的強度 | 似た種類の処理をまとめたもの | フラグで処理を切り替える |
| 時間的強度 | 同じタイミングで実行する処理 | 初期化処理など |
| 手順的強度 | 順番に実行する必要がある処理 | ステップ1→2→3 |
| 連絡的強度 | データを共有する処理 | 前の結果を次に渡す |
| 情報的強度 | 同じデータを操作する処理 | CRUD操作 |
| 機能的強度 | 単一の明確な目的を持つ処理 | 1つのことだけをする |
初めて見たときは、「で、これがなに?」って思っていました。
いつかわかるときがくるだろうから、「モジュール強度は強い方がいい」とだけ覚えておこうと。
単一責任の原則
ここで、モジュール強度を理解するうえでの重要な考え方として、単一責任の原則を確認しておきます。
プログラムを設計するときの基本理念の一つに、単一責任の原則があります。
(基本理念とは、プログラムを書いていくときに、こういう考えをベースにしようねっていうものです。)
単一責任の原則は、
モジュール(メソッドやクラスなど)が、「たった一つの理由」で変更されるべきである。
という考え方です。
変更の理由が一つに絞られているモジュールは、何を責務としているのかが明確になり、その結果、コードの保守性や可読性が向上します。
逆に、変更の理由が複数あるモジュールは、責務が曖昧になりやすく、保守性や可読性が低下します。
モジュール強度と単一責任の原則の関係
では、モジュール強度と単一責任の原則はどう関係しているのでしょうか。
モジュール強度が高い場合
モジュール内の処理が"強く"関連
↓
モジュールは「ひとつのこと」をしている
↓
責務が明確で、変更の理由が一つ
↓
単一責任の原則に従っている
モジュール強度が低い場合
モジュール内の処理が"弱く"関連
↓
モジュールは「複数のこと」をしている(何をしているかはっきり言えない)
↓
責務が曖昧で、変更理由が複数
↓
単一責任の原則から逸れている
つまり、モジュール強度が低い = 責務が曖昧 = 単一責任の原則から逸れている ということができます。
ここまで、モジュール強度と単一責任の原則について、一般的な説明をしてきました。
では、実際の開発では、どのようにモジュール強度が低くなってしまうのでしょうか。
Railsアプリの開発コードを例に見ていきます。
Fat Model
Railsは、MVCパターンを採用していて、それぞれが役割を持っています。
たとえばモデルは、扱うリソース(UserやArticleなど)のルールやロジックを担っています。
単一責任の原則からいうと、Userモデル(user.rb)が変更されるときの理由は、
「Userのルールやロジックに関すること」であるべきです。
しかし開発していくと、
「とりあえず定義したメソッド」、
「ビューで表示するためのメソッド」など、
本来のモデルの役割とは違うメソッドも増えていってしまいます。
その状態が、Fat Modelです。
では、「どれくらいFatなのか」を客観的に判断するにはどうすればよいでしょうか。
そこで使えるのがモジュール強度です。
モジュール強度は、モジュール内の処理同士の関連性を評価する指標です。
強度が弱いほど、「関係の薄い処理が寄せ集められている」状態を意味します。
つまり、モジュール強度が弱い = Fat Model の可能性があると判断できるわけです。
モジュール強度が弱いコード例
モジュール強度が弱い状態のモデルを見てみます。
※イメージをつかむ例としてわかりやすくするためにコードの量は少なくしています。
class User < ApplicationRecord
# 表示用ロジック
def display_name
nickname.presence || email
end
# 状態判定
def active?
deleted_at.nil?
end
# 表示用ロジック
def welcome_message
"ようこそ、#{display_name}さん"
end
end
これらは「Userに関する処理」という漠然とした共通点だけでまとめられており、処理同士の関連性は弱いです。
このような「似たような雰囲気」でまとめられた状態を、論理的強度といいます。
(2番目にモジュール強度が弱い状態です。)
では、このUserモデルの何が問題なのでしょうか。
責務を一言で説明できない
こちらのUserモデルは、責務を一言で説明できません。
- active?メソッドで、Userの状態を管理する
- display_nameメソッドで、Userの表示名を決める
- welcome_messageメソッドで、表示用の文言を生成する
「これら3つのメソッドが含まれる、Userモデルの責務は...。」
本来のUserモデルは、Userというリソースの状態やルールを表すものです。
表示に関するメソッドが増えてしまったことで、責務が曖昧となってしまいました。
「Userに関する処理」という、なんか似ているような雰囲気のある集まりになっています。
変更理由を考える
先ほどは「Userモデルの責務は何か」から見て、責務が曖昧になっていることがわかりました。
今度は、このモデルの変更理由を見てみます。
単一責任の原則からいえば、モジュールの変更理由はひとつであるべきでした。
しかし、表示に関する処理がUserモデルに含まれていることで、
「Userの状態・ルールの変更」だけでなく「表示の仕様変更」という異なる理由で、Userモデルが変更されることになります。
つまり、単一責任の原則がいう「変更の理由が一つ」から逸脱していることがわかります。
改善案
では、このUserモデルはどのような改善ができるでしょうか。
class User < ApplicationRecord
# Userの状態判定のみを責務とする
def active?
deleted_at.nil?
end
end
class UserPresenter
def initialize(user)
@user = user
end
def display_name
@user.nickname.presence || @user.email
end
def welcome_message
"ようこそ、#{display_name}さん"
end
end
このように分離することで、それぞれのモジュールが、明確な責務を持つようになりました。
分離したモジュールの責務と変更理由を整理すると、次のようになります。
Userモデル
責務:Userの状態やルールを管理
変更理由:Userのビジネスルールの変更
モジュール強度:機能的強度(高い)
UserPresenter
責務:Userの表示ロジック
変更理由:表示仕様の変更
モジュール強度:情報的強度〜機能的強度(高い)
おわりに
- モジュールの責務を一言で説明できなくなったとき
- モジュールの変更理由が複数思い浮かぶようになったとき
それは設計を見直すサインです。
意識して設計していても、開発が進むにつれて、責務が少しずつ曖昧になることは避けられません。
そのときに、
「なぜ分かりにくくなってきたのか」
「どのような理由で単一責任の原則から逸れているのか」
を整理するための視点が、モジュール強度です。
モジュール強度は、「コードの良い・悪いを決めるための絶対的なルール」ではありません。
しかし、モジュールの状態を客観的に説明するための共通言語として役立ちます。
モジュール強度を理解していると、
「なぜこのコードは分かりにくいのか」が言語化できて、
他の人とも共有しやすくなるんだろうなと思います(チーム開発は未経験なのでイメージですが)。
最後まで読んでいただき、ありがとうございました。