🦜

ソフトウェアアーキテクチャの基礎 (第3章)

に公開

量が多いので分割
これの続き
https://zenn.dev/zennkey/articles/a53b3e298f09c7

3章モジュール性

コードの再利用メカニズムは様々だが、関連するコードをモジュールにまとめられる。

アーキテクチャを分析する際に使うツール(メトリクス、適応度関数、可視化など)の多くはモジュール性に依存している。

モジュール性は変性の原理を成す。

優れたモジュール性を維持することは、暗黙的なアーキテクチャ特性となっている。

3.1 定義

モジュールとは?

→「より複雑な構造を構築するために使用できる標準化された部品または独立した単位の各集合」(一般)

本書では、関連するコードのグループ化を表す一般的な用語としてモジュールという用語を使用している。

3.2 モジュール性の計測

3.2.1 凝集度(Cohesion)

モジュールの要素がどの程度そのモジュールに収まっているかの度合いを表す

→モジュール内の要素同士の関連度を表す指標

関連する要素が全て1箇所にまとまっている状態が、凝集したモジュールの理想

Larry Constantine

「凝集度の高いモジュールを分割しようとしても、結合が増えて可読性が低下するだけだ」

凝集度の尺度の定義

  • 機能的凝集(Functional Cohesion) : 関連する要素だけでモジュールが構成され、モジュールが機能するために必要不可欠なものが全て含まれている
  • 逐次的凝集(Sequential Cohesion) : 一方が出力したデータを、もう一方が入力する形で2つのモジュールが相互に作用している
  • 通信的凝集(Communicational Cohesion) : 2つのモジュールが通信の連鎖を形成し、それぞれの情報を操作したり、何らかの出力に貢献したりする
  • 手続き的凝集(Procedural Cohesion) : 2つのモジュールは、特定の順序でコードを実行する必要がある
  • 時間的凝集(Temporal Cohesion) : モジュール間に時間的な依存関係がある
  • 論理的凝集(Logical Cohesion) : モジュール内のデータは論理的に関連しているも、機能的には関連していない状態
  • 偶発的凝集(Coincidental Cohesion) : モジュール内の要素は、同じソースファイル内にある以外に関連性がない

7段階の尺度があるが、凝集度は結合度よりも精度の低いメトリクスである

アーキテクトはモジュールの構成においてトレードオフ分析を行いながら、凝集度(具体的には凝集度の欠如)を判断する

LCOM(Lack of Cohesion in Methods メソッドの凝集度の欠如)

\text{LCOM} = \begin{cases} P - Q & \text{if } P > Q \\ 0 & \text{otherwise} \end{cases}

LCOM96b (ver. 2)

\text{LCOM96b} = \frac {1}{a}\sum_{j=1}^{a} \frac{ m -\mu(A_j)}{m}

モジュールの構造的な凝集度を計測するメトリクス

アーキテクチャを移行する際にコード分析をしているアーキテクトに役立つ指標

偶発的に結合されたクラスや、そもそも1つのクラスであるべきではなかったクラスを検知できる

このメトリクスが見つけられるのは、構造的な凝集度の欠如だけである

(要素が適切に組み合わされているかを論理的に判断できない)

3.2.2 結合度

数学的な分析する概念

求心性結合(Afferent coupling)

コードアーティファクト(コンポーネント、クラス、関数)に外部から入力される接続数を計測する

遠心性結合(Efferent coupling)

他のコードアーティファクトに出力する接続数を計測する

3.2.3 抽象度、不安定度、主系列からの距離

抽象度(Abstractness)

抽象的なアーティファクト(抽象クラス、インターフェースなど)と具体的なアーティファクト(実装)クラスの比率のことで、抽象と実装の比率を表す

\text{A} = \frac {\sum m^a}{\sum m^c + \sum m^a}

不安定度(Instability)

コードベースの不安定さを判断するもの

不安定度の高いコードベースは結合の度合いが高く、変更された時に壊れやすい

求心性結合と遠心性結合の合計に対する遠心性結合の比率として求められる

不安定度が高い = 多くの外部コンポーネントに依存している

→依存先に変更があった場合、自分も変更しなければいけなくなる可能性が高い

\text{I} = \frac {C^e}{Ce +C^a}

3.2.4 主系列からの距離

主系列からの距離(Distance from the main sequence)

アーキテクチャ上の構造についてアーキテクトが手にできている数少ない総合的なメトリクスの1つ

不安定度と抽象度から求められる

D = |A + I - 1|

主系列とは、抽象度と不安定度の理想的なバランスを示すもの(A+I = 1 → D = 0)

二つのゾーン

  • 苦痛ゾーン (Zone of Pain)
    • AとIが0に近い場合
    • 抽象度が低い(具体である)が安定している
    • 変更が難しく多くのコンポーネントに影響を与える
  • 無駄ゾーン (Zone of Useless)
    • AとIが1に近い場合
    • 抽象度が高く不安定である状態

3.2.5 コナーセンス

コナーセンス(Connascence)

システムの全体的な正しさを維持するため、あるコンポーネントの変更が別のコンポーネントの変更を必要とする場合、2つのコンポーネントはコナーセント(接続)している

「静的」と「動的」の2種類のコナーセンスが提唱されている

3.2.5.1 静的なコナーセンス

コードレベルでの結合を指す

  • 名前のコナーセンス(Connascence of Name : CoN)
    • 名前を変更したら、その名前を使っている箇所を全て変更しなければいけない関係
    • 最も望ましい結合の方法
  • 型のコナーセンス(Connascence of Type : CoT)
    • データの型が一致していなければいけない関係
  • 意味のコナーセンス(Connascence of Meaning : CoM)
    • 複数の場所で、同じ値が同じ意味を持つことに依存している関係
    • マジックナンバーや文字列を避けるという理論的根拠になるもの
  • 位置のコナーセンス(Connascence of Position : CoP)
    • 要素の順序・位置が重要で、それに依存している関係
    • 順序を変更することで正しく動かなくなったり、意味が変わってしまう関係のこと
    • 引数の順序や配列のインデックス指定など
  • アルゴリズムのコナーセンス(Connascence of Algorithm : CoA)
    • 複数の場所で、同じアルゴリズムや処理順序を共有しなければならない関係

3.2.5.2 動的なコナーセンス

実行時の呼び出しを分析するもの

  • 実行順序のコナーセンス(Connascence of Execution)
    • 複数の処理の実行順序が重要で、その順序に依存している関係
  • タイミングのコナーセンス(Connascence of Timing: CoT)
    • 複数の処理の実行タイミングが重要で、タイミングに依存している関係
  • 値のコナーセンス(Connascence of Value : CoV)
    • 複数の値が互いに関連していて、それらの値の整合性を保たなければいけない関係
  • アイデンティティのコナーセンス(Connascence of Identity: CoI)
    • 複数のコンポーネントが、あるエンティティの特定のインスタンスに依存している関係

3.2.5.3 コナーセンスの性質
強さ
開発者がその主のコナーセンスをどれだけ簡単にリファクタできるかという観点で、コナーセンスの強さを評価することができる
より良いコナーセンスに向けてリファクタすることで、コードベースの結合特性を改善できる
動的なコナーセンスより静的なコナーセンスを好むべきである
→静的なコナーセンスであればコード解析によって改善点を見つけることが可能である

以下の優先度順でリファクタを目指す
1. 名前
2. 型
3. 意味
4. アルゴリズム
5. 位置
6. 実行順序
7. タイミング
8. 値
9. アイデンティティ

位置関係
結合している要素同士が、コードベース上でどれくらい離れているかを表す性質
強いコナーセンスは近くに、弱いコナーセンスは遠くに配置する(変更の影響範囲を小さくすることができる)
同一のコナーセンスでも、より近くに配置されているものの方がコナーセンスは強くなる

度合い
 コナーセンスの度合いは、その影響の大きさである
 度合いが低ければ低いほどコードベースのダメージは低くなる
 モジュール数が少なければ、動的なコナーセンスがあったとしてもそこまで酷くはならない
 コードベースが大きくなればなるほど、問題も大きくなる

コナーセンスを使ってシステムのモジュール性を向上させる3つのポイント

  1. カプセル化された要素にシステムを分割することで、全体的なコナーセンスの度合いを最小に抑えること
  2. カプセル化の境界を跨ぐコナーセンスの度合いは、いかなるものであれ最小に抑えること
  3. カプセル化の境界内では、コナーセンスの度合いを最大化すること

アドバイス
度合いのルール : 強いコナーセンスを弱いコナーセンスに変換しよう
位置関係のルール:ソフトウェア要素間の距離が遠くなるにつれ、より弱いコナーセンスを使用しよう

Discussion