そろそろ、冒頭の挨拶がコピペっぽくなってきましたが、皆さんいかがお過ごしでしょうか?
前回は、継承の深さ について説明をしました。
残すところ、このシリーズも今回を含め2回。
今回も、個々のメトリックス値がどのように変化していくのか、をテーマに クラス結合度 について説明します。
Explanation
おさらいですが、クラス結合度はMSDNのコード メトリックス値というページでの説明によると、
クラス結合度 - パラメーター、ローカル変数、戻り値の型、メソッド呼び出し、ジェネリックまたはテンプレートのインスタンス化、基本クラス、インターフェイス実装、外部の型で定義されたフィールド、および属性による装飾を使用して、一意のクラスへの結合度を測定します。 適切なソフトウェア デザインでは、型およびメソッドの連携は高まり、結合性は低くなります。 結合性が高いということは、他の型への依存関係が多いために再利用や保守が困難なデザインであることを示します。
と定義されています。
ちょっと今回は難しそうです (サンプルコードの準備的な意味で)
今回も簡単にソースコードを実査に観察して、クラス結合度が変化する様子をソースコードを用いて観察してみましょう。
Exercise
今回のサンプルソースは下記です。
今回も管理人の趣味が多分に発揮されていますが無視してください。
1 | namespace CodeMetricsTest |
これを Metrics.exe で計測してみましょう。
下記はメトリックスの結果のうち、Family クラス、FireVampire クラスに着目した表です。
メトリックス項目 | Family | FireVampire |
---|---|---|
MaintainabilityIndex (保守容易性指数) |
100 | 100 |
CyclomaticComplexity (サイクロマティック複雑度) |
1 | 1 |
ClassCoupling (クラス結合度) |
0 | 1 |
DepthOfInheritance (継承の深さ) |
1 | 2 |
LinesOfCode (コード行) |
1 | 1 |
Family クラス は、メンバに何もありません。
しかし、継承の深さが 1 なのは前回説明したとおりですが、クラス結合度は 0 です。
これは何を意味しているのでしょうか? この結果についての解がこちらに記述されています。
クラス結合度 - パラメータ、ローカル変数、戻り値、メソッド呼び出し、ジェネリックまたはテンプレートのインスタンス化、基本クラス、インターフェイス実装、外部の型で定義されたフィールド、および属性による装飾を使用して、一意のクラスへの結合度を測定します。計算では、int32、string、object など、プリミティブ型や組み込み型は除外します。 適切なソフトウェア デザインでは、型およびメソッドの連携は高まり、結合性は低くなります。結合性が高いということは、他の型への依存関係が多いために再利用や保守が困難なデザインであることを示します。
つまり、全てのクラスの継承元である Object クラス は結合度の計算から除外するわけですね。
他にも、プリミティブ型や組み込み型が除外されるとありますが、これらはこちらのページの組み込みのデータ型の章で確認できます。
FireVampire クラス も、メンバに何もありませんが、クラス結合度が 1 なのは、Family クラス を継承しているから +1 なのでしょう。
次は、Cthugha クラス、Nyarlathotep クラスに着目した表です。
メトリックス項目 | Cthugha | Nyarlathotep |
---|---|---|
MaintainabilityIndex (保守容易性指数) |
97 | 97 |
CyclomaticComplexity (サイクロマティック複雑度) |
2 | 2 |
ClassCoupling (クラス結合度) |
2 | 1 |
DepthOfInheritance (継承の深さ) |
2 | 2 |
LinesOfCode (コード行) |
2 | 2 |
Nyarlathotep クラス は前述の FireVampire クラス と同じです。
プロパティの String クラス、継承元の Object クラスは計算から除外されており、計算対象は継承元の GreatOldOne クラス のみが計算対象です。
問題は、Cthugha クラス です。
メンバに FireVampire クラス の配列を保持しています。
ここで1つ疑問が浮かびます。
クラス結合度の計算対象には、Object クラスを除外した継承元のクラスは計算対象になるのは前述の通りです。
では、この継承元のクラス、というのは、パラメータ、ローカル変数、戻り値、メソッド呼び出し、ジェネリックまたはテンプレートのインスタンス化、インターフェイス実装、外部の型で定義されたフィールド、および属性による装飾 にも適用されるのでしょうか? 結論から言えば答えは No です。
なぜなら、Cthugha クラス のクラス結合度は、継承元の GreatOldOne クラス、メンバに存在する配列の宣言型である FireVampire クラス の 1 + 1 = 2 というのがその答えです。
つまり、その継承元である Family クラス は計算に入っていないことになります。
その証拠に次の2つのソースではクラス結合度が異なります。
1 | namespace CodeMetricsTest |
1 | namespace CodeMetricsTest |
表は省略しますが、Cthugha クラス のクラス結合度は前者が 1、後者は 2 となります。
Conclusion
まとめると、クラス結合度 は、
- プリミティブ型や組み込み型である Byte,SByte,Int32,UInt32,Int16,UInt16,Int64,UInt64,Single,Double,Char,Boolean,Object,String,Decimal はクラス結合度の計算から除外される。
- クラスの継承がクラス結合度の計算に影響するのは、クラスの定義の際のみ。ローカル変数やメンバ等で使用した型の継承元まではクラス結合度にカウントされない。
- クラスの継承で、カウントされるのは直接の親となるクラスのみ。
クラス結合度を低下させるには、ポリモーフィズムを活用して、直接依存するクラスを減らす方法が挙げられます。
また、凝集度を高めることは結合度の低下にも繋がりやすいです。
今回も長くなりましたが、クラス結合度がどういうものかわかりました。
次回は コード行 について、ソースコードを交えながら説明します。