前回は、サイクロマティック複雑度 について説明をしました。

今回も、個々のメトリックス値がどのように変化していくのか、をテーマに 継承の深さ について説明します。

Explanation

おさらいですが、継承の深さはMSDNのコード メトリックス値というページでの説明によると、

継承の深さ - クラス階層構造のルートまでのクラス定義の数を示します。
階層構造が深くなるにつれて、特定のメソッドおよびフィールドが定義または再定義されている場所を把握することがより困難になる場合があります。

と定義されています。

では、ありきたりな展開ではありますが…. 今回も簡単にソースコードを実査に観察して、継承の深さが変化する様子をソースコードを用いて観察してみましょう。

Exercise

今回のサンプルソースは下記です。
管理人の趣味が多分に発揮されていますが無視してください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
namespace CodeMetricsTest
{
class Program
{
static void Main(string[] args)
{
}
}

class Human
{
}

abstract class Student : Human
{
public abstract int Level
{
get;
}
}

interface IImagineBreaker
{
void Sogebu(ISupernaturalPower power);
void Sogebu(IMagic magic);
}

class KamijoToma : Student, ISupernaturalPower, IImagineBreaker
{
public void Sogebu(ISupernaturalPower power)
{
System.Console.WriteLine("その幻想をぶち殺す!");
}

public void Sogebu(IMagic magic)
{
System.Console.WriteLine("その幻想をぶち殺す!");
}
public void DoSupernaturalPower()
{
System.Console.WriteLine("上条さんはレベル0ですよ");
}

public override int Level
{
get { return 0; }
}
}

interface ISupernaturalPower
{
void DoSupernaturalPower();
}

interface IAccelerator : ISupernaturalPower
{
}

class Accelerator : Student, IAccelerator
{
public void DoSupernaturalPower()
{
System.Console.WriteLine("ベクトル変換ってなァ");
}

public override int Level
{
get { return 5; }
}
}

interface IMagic
{
void DoMagic();
}

interface IAutoReverse : ISupernaturalPower
{
}

class TsuchimikadoMotoharu : Student, IAutoReverse, IMagic
{
public void DoSupernaturalPower()
{
System.Console.WriteLine("肉体再生だにゃ");
}

public void DoMagic()
{
System.Console.WriteLine("赤ノ式ぜよ");
}

public override int Level
{
get { return 0; }
}
}

}

今回はいつにも増して残念極まりないソースですが、これを Metrics.exe で計測してみましょう。

下記はメトリックスの結果のうち、クラスに着目した表です。

メトリックス項目 Human Student KamijoToma Accelerator TsuchimikadoMotoharu
MaintainabilityIndex
(保守容易性指数)
100 100 95 96 95
CyclomaticComplexity
(サイクロマティック複雑度)
1 1 5 3 4
ClassCoupling
(クラス結合度)
0 1 5 4 5
DepthOfInheritance
(継承の深さ)
1 2 3 3 3
LinesOfCode
(コード行)
1 1 5 3 4

次は、メトリックスの結果のうち、インターフェイスに着目した表です。

メトリックス項目 IImagineBreaker ISupernaturalPower IAccelerator IMagic IAutoReverse
MaintainabilityIndex
(保守容易性指数)
100 100 100 100 100
CyclomaticComplexity
(サイクロマティック複雑度)
2 1 0 1 0
ClassCoupling
(クラス結合度)
2 0 1 0 1
DepthOfInheritance
(継承の深さ)
0 0 0 0 0
LinesOfCode
(コード行)
0 0 0 0 0

そして、最後は、上記サンプルのクラスダイアグラム図になります。
図中の DI は Depth of Inheritance (DI) の略です。

前述の定義通りメトリックスの結果から、継承の深さは、クラス階層構造のルートである Object クラス までのクラスの数であることを証明してくれました。

ちなみに、Metrics.exeで mscorlib.dll を計測し、Object クラス のメトリックスを確認すると、継承の深さは 0 でした。

また、もうお気づきでしょうが、継承の深さの計算に、インターフェイスは含まれていません。 インターフェイスはクラスに実装するものですし。継承とは言わないのでしょう。
でも、インターフェイスAを継承したインターフェイスBという実装もあるのですが、これも継承の深さにカウントされません。
前述の 「クラス階層構造のルートまでのクラス定義の数」 という定義をそのまま素直に受け止めればクラスではないインターフェイスは無関係なのでしょう。

Conclusion

まとめると、継承の深さは、

  • 継承を一切していないクラスを定義しても、1 としてカウントされる。
  • インターフェイスを実装しても継承の深さに影響しない。
  • インターフェイス自身が、別のインターフェイスを継承していたとしても継承の深さは常に 0 である。

継承の深さを低下させるには、全体的な設計の見直しが挙げられます(というかこれ以外あります?) ただ、継承の深さにそこまで過敏になる必要はないのでは?と個人的には思います。
例えば、System.Windows.Control クラス を継承してカスタムコントロールを作成した時点で、継承の深さは 4 になってしまいます。
ともすれば、既存のクラスを再利用しようとすると、継承の深さは避けては通れない問題ではないのかなぁ、と思ってしまいます。

今回も長くなりましたが、継承の深さがどういうものかわかりました。
次回は クラス結合度 について、ソースコードを交えながら説明します。