【Unity C#】静的コード解析ツールを使ってみた

# はじめに
静的コード解析ツール**SourceMonitor**を使ってみました。

# 静的コード解析ツール？
ソースコードの**汚さ**を解析できます。

参考: <a href ="https://ja.wikipedia.org/wiki/静的コード解析">静的コード解析 - Wikipedia</a>

# なぜ使うのか
ソースコードが読みやすいのか、読みにくいのかといった事はソースコードを読むまで分かりません。
静的解析を使うことで、ソースコードを**読まなくても**ソースコードがキレイなのかどうかがある程度分かるようになります。

# 採用例
ゲーム会社の**フロム・ソフトウェア**あたりでも静的解析が使われているみたいです。<a href="https://www.inside-games.jp/article/2013/08/30/69939.html">
【CEDEC2013】静的解析で開発現場はどう変わったのか――自動化が変えたソフトウェア品質</a>

# C#ソースコードの準備
解析を行う前の準備としてNewBehaviourScript.csが１つだけ追加されたUnityプロジェクトを用意しておきます。

UnityのバージョンはなんでもOKです。 **ソースコードがあればOK**。

```NewBehaviourScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}
```

# SourceMonitorを使ってみる
## 0. ダウンロード
下記URLからSourceMonitorをダウンロードしてインストールしてください
http://www.campwoodsw.com/sourcemonitor.html

![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/769d14a7-4aba-6885-bd82-10954027e0ed.png)

## 1. SourceMonitorを起動
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/53a23224-3f41-81c3-887a-29525e509464.png)

## 2. 新規プロジェクト作成
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/777ad600-a315-8c98-4ed1-d2d093fca501.png)


## 3. 解析したい言語を選ぶ
今回はUnity C#のソースコードを解析したいので、 **C#**を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/7d4f06b9-61e3-9483-d038-7be89ac3a4c3.png)

## 4. ソースコードのディレクトリの指定
プロジェクト名とソースコードが置いてある場所のディレクトリを入力します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/ae939da3-30c5-0250-e0aa-9362f24abaae.png)

## 5. サブディレクトリの指定
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/bd25d1f6-d38c-9b65-661b-8246b8fd198f.png)

## 6. プロジェクト設定
今回は何も触らずに "次へ"を押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/851cc1b3-89e4-e0c8-b21c-173eb1eec158.png)

"次へ"を押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/e0f3d7f3-9470-e17d-f4d7-80a86bfa600a.png)

## 7. チェックポイント設定
**Allow parsing of UTF-8 files**のチェックが外れていると、
UTF-8形式のソースコードが**解析できない**のでチェックを入れておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/b876dabf-5b5d-5ac6-b933-16d619589f86.png)

## 8.完了
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/21310cdf-6697-46c7-fea7-2eb6e67c2db4.png)

完了ボタンを押すとプロジェクト作成は完了です。

## 9.さっそく解析してみる
プロジェクト作成が完了すると、チェックポイント特定ウィンドウが表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/72c31f5c-4cca-75c8-75e1-d353e67d1722.png)

閉じてしまった場合はメニューのNew Checkpoint...からチェックポイント特定ウィンドウを開くことができます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/3cd23ce5-aad9-5845-9ce3-4fabed4b695f.png)

## 10.解析結果
OKボタンを押すと、解析結果が表示されます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/d7d9f8eb-f9e9-927d-027e-b95a95e28fce.png)

## 解析結果
出来たてのNewBahaviourScript.csを１個解析した結果は以下のようになりました。

|項目名  |意味  |数値 |
|---|---|--:|
| Files | ファイル数 |1|
| Lines | 空行も含んだコード行数 |16|
| Statements | 空行を除いたコード行数 |6|
| Mathods/Class| クラス１個あたりのメソッド数 |2.00|
| Calls/Method|メソッド呼び出し回数をメソッドの個数で割った値|0.00|
| Max Complexity | 複雑度の最大値 |1|
| Max Depth |	ネストの最大値 |1|
| Avg Depth	| ネストの平均値 |0.33|
| Avg Complexity | 複雑度の平均値 |1.00|

# 結局どういう値なの？
解析結果の数値が分かりましたが、これらの数値がどのような意味を持つかがいまいち**ピンと来ない**。
そこで、ソースコードを**一部変更**してもう一度**解析**してみます。<br>

# 検証1: if文を追加して静的コード解析してみる
NewBehaviourScript.csを以下のように編集してみます。

```NewBehaviourScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {

        if (Time.time > 1f)
        {
            Debug.Log("ゲーム開始から1秒以上経過しました");
        }
		
	}
}
```

ゲーム開始から1秒以上経過した場合にデバッグログを出すようにスクリプトを変更してみました。

## 検証1: 解析結果
このNewBehaviourScript.csを解析した結果は以下のようになりました

![image.png](https://qiita-image-store.s3.amazonaws.com/0/44288/51a88a77-fe05-46dd-51b5-564ca2927120.png)

|項目名  |意味  |数値 |
|---|---|--:|
| Files | ファイル数 |1|
| Lines | 空行も含んだコード行数 |21|
| Statements | 空行を除いたコード行数 |8|
| Mathods/Class| クラス１個あたりのメソッド数 |2.00|
| Calls/Method|メソッド呼び出し回数をメソッドの個数で割った値|0.50|
| Max Complexity | 複雑度の最大値 |2|
| Max Depth |	ネストの最大値 |3|
| Avg Depth	| ネストの平均値 |0.88|
| Avg Complexity | 複雑度の平均値 |1.50|

# 解析結果を比較してみる

|項目名  |意味  |最初 |検証1 |
|---|---|--:|--:|
| Files | ファイル数 |1|1|
| Lines | 空行も含んだコード行数 |16|21|
| Statements | 空行を除いたコード行数 |6|8|
| Mathods/Class| クラス１個あたりのメソッド数 |2.00|2.00|
| **Calls/Method**|メソッド呼び出し回数をメソッドの個数で割った値|**0.00**|**0.50**|
| **Max Complexity** | 複雑度の最大値 |**1**|**2**|
| Max Depth |	ネストの最大値 |1|3|
| Avg Depth	| ネストの平均値 |0.33|0.88|
| Avg Complexity | 複雑度の平均値 |1.00|1.50|

# 考察
以下のようなことが読み取れます。
・メソッド呼び出し回数が増加
・複雑度が増加

#考察 - 複雑度の増加について
表にて"複雑度"というワードが出てきますが、これは**サイクロマティック複雑度**を表しているそうです。

参考: <a href="http://blog.jnito.com/entry/20101231/1293770272">数値で測るコード品質 - give IT a try</a>

このサイクロマティック複雑度(循環的複雑度)ですが、
**条件分岐が１回**登場するたびに複雑度は**１増加**するそうです。

つまり、下記のif文が原因で複雑度の最大値が1から2へ増加していたようです。

```NewBehaviourScript.cs
if (Time.time > 1f)
{
    Debug.Log("ゲーム開始から1秒以上経過しました");
}
```

## 参考URL
数値で測るコード品質
http://blog.jnito.com/entry/20101231/1293770272

循環的複雑度を活かしたバグ潜在リスクの軽減
http://szk-takanori.hatenablog.com/entry/20111219/p1


# 検証2: if文の条件を増やしてみる
if文の中身の条件式を増やして静的コード解析してみます。

```csharp

        if (Time.time > 1f && Time.time < 2f)
        {
            Debug.Log("ゲーム開始から1秒以上経過しました");
        }
```

## 検証2: 解析結果

|項目名  |意味  |数値 |
|---|---|--:|
| Files | ファイル数 |1|
| Lines | 空行も含んだコード行数 |24|
| Statements | 空行を除いたコード行数 |8|
| Mathods/Class| クラス１個あたりのメソッド数 |2.00|
| Calls/Method|メソッド呼び出し回数をメソッドの個数で割った値|0.50|
| Max Complexity | 複雑度の最大値 |**3**|
| Max Depth |	ネストの最大値 |3|
| Avg Depth	| ネストの平均値 |0.88|
| Avg Complexity | 複雑度の平均値 |2.00|

今度は複雑度が**3**になりました。
if内部の**条件式が増えると複雑度も増える**、ということが読み取れます。

# 検証3: ifを入れ子にしてみる
ifで判定している箇所を以下のように変更して、静的コード解析してみます。

```NewBehaviourScript.cs
        if (Time.time > 1f)
        {
            if (Time.time < 2f)
            {
                Debug.Log("ゲーム開始から1秒以上経過しました");
            }
        }
```

## 検証3: 解析結果

|項目名  |意味  |数値 |
|---|---|--:|
| Files | ファイル数 |1|
| Lines | 空行も含んだコード行数 |27|
| Statements | 空行を除いたコード行数 |9|
| Mathods/Class| クラス１個あたりのメソッド数 |2.00|
| Calls/Method|メソッド呼び出し回数をメソッドの個数で割った値|0.50|
| Max Complexity | 複雑度の最大値 |3|
| Max Depth |	ネストの最大値 |4|
| Avg Depth	| ネストの平均値 |1.22|
| Avg Complexity | 複雑度の平均値 |2.00|

# 検証2と検証3を比較してみる
ここで、検証2と検証3を比較して表にまとめてみました。

|項目名  |意味  |検証2(ifに条件式二つ) |検証3(ifの入れ子) |
|---|---|--:|--:|
| Files | ファイル数 |1|1|
| Lines | 空行も含んだコード行数 |24|27|
| Statements | 空行を除いたコード行数 |8|9|
| Mathods/Class| クラス１個あたりのメソッド数 |2.00|2.00|
| Calls/Method|メソッド呼び出し回数をメソッドの個数で割った値|0.50|0.50|
| Max Complexity | 複雑度の最大値 |3|3|
| Max Depth |	**ネストの最大値** |**3**|**4**|
| Avg Depth	| ネストの平均値 |0.88|1.22|
| Avg Complexity | 複雑度の平均値 |2.00|2.00|

**入れ子**を作ると解析結果の**ネストの深さ**も上がる、ということが読み取れます。
複雑度は変わっていないことも読み取れます。

# ネストについて
深いネストを作るとソースコードが読みづらくなるとよく言われます。
(参考: <a href = "https://qiita.com/Mic-U/items/1ec901864d4ab11c8d6f">ネストの深さは闇の深さ</a>)

解析結果のDepthが大きい数値になるようであれば、ソースコードを見直したほうが良さそうです。

# まとめ
・**Complexity** は分岐の数。　この数値が大きいと**設計**に**問題**がある可能性が高い

・**Depth**はネストの深さ。　この数値が**大きいと読みづらい**ソースコードに

# 参考URL
複雑度と単体テストケース数の相関関係
http://forza.cocolog-nifty.com/blog/2009/01/post-ca39.html

ネストの深さは闇の深さ
https://qiita.com/Mic-U/items/1ec901864d4ab11c8d6f

メトリクス計測 SourceMonitor 紹介
https://qiita.com/syougun360/items/f38981dba32d3afd2957