Quantcast
Channel: t-hom’s diary
Viewing all articles
Browse latest Browse all 493

VBA 循環的複雑度という指標でプロシージャの複雑度を測ってみる

$
0
0

VBAに限らずすべての言語に言えることだが、プロシージャが複雑になればなるほどバグが混入しやすくなる。

そこで今回はプロシージャの複雑度を測る「循環的複雑度」という指標を紹介する。
何がどう循環的なのか知らないけど、名前が醸し出すややこしさとは反対に、とてもシンプルに計算できるので身構える必要はない。

まず押さえておきたいのは、この指標は10以下が理想的で、30を超えるとヤバいということ。

数え方

以下の参考サイトに次の表記がある。

関数の循環的複雑度 = 1 + 関数の中に以下のものが出てきた回数:
if、while、for、foreach、case、default、continue、goto、&&、||、catch、?: (三項演算子)、??

参考:D.O.R.Y. : Dory Offers a Room for You: 循環的複雑度 ( Cyclomatic Complexity ) とは何ぞや


つまりこれをVBAの用語に翻訳すると、

プロシージャの循環的複雑度 = 1 + プロシージャの中に以下のものが出てきた回数:
If、ElseIf、Do、For、For Each、Case、GoTo、And、Or、Wend、IIf、Switch、Choose

ということになる。

※WhileではなくWendとしたのは、Do文のWhileキーワードと、While~Wend文のWhileキーワードが別物だから。
※Caseは、Select Case文の中身のCaseだけ数える。Selectキーワードの直後に来るCaseはノーカウント。

抜け漏れがあるかもしれないけど、主だったものは概ねカバーできてるはず。

勘違いしやすいポイントだが、Elseはカウントしない。
なぜならElseが明示的に書かれようと書かれまいと、分岐経路の数は変わらないから。
f:id:t-hom:20180809013032p:plain

※ElseIfは別途経路が発生するのでカウントする。

ForやDoなどの繰り返しも、終了条件を満たしたらスキップされるという点で、分岐処理の一種といえる。
イメージしにくければ、最初から終了条件を満たしていればループの内部は一切通らないという点を考えてみると良い。

AndやOrは条件を集約しているが、以下のように分解して考えると経路の分岐にカウントされる理由が分かると思う。

↓Andの場合

'元のコードIf A And B Then
    Result1
Else
    Result2
EndIf'条件を分解したコードIf A ThenIf B Then
         Result1
    Else
         Result2
    EndIfEndIf

↓Orの場合

'元のコードIf A Or B Then
    Result1
Else
    Result2
EndIf'条件を分解したコードIf A Then
    Result1
ElseIf B Then
    Result1
Else
    Result2
EndIf

具体例

今私が作ってるマクロのプロシージャを例に循環的複雑度を測ってみる。
コードはこちら。

Sub ChangeProcessType(TargetShape As Shape, T As MsoAutoShapeType)Dim processConnectors AsNew Collection
    Dim s As Shape
    
   '現在TargetShapeに接続されたコネクタを一覧化しておくForEach s In TargetShape.Parent.Shapes
        If s.Connector ThenIf s.ConnectorFormat.BeginConnected ThenIf s.ConnectorFormat.BeginConnectedShape Is TargetShape Then
                    processConnectors.Add _Array(s, s.ConnectorFormat.BeginConnectionSite,True)'True=BeginEndIfEndIfIf s.ConnectorFormat.EndConnected ThenIf s.ConnectorFormat.EndConnectedShape Is TargetShape Then
                    processConnectors.Add _Array(s, s.ConnectorFormat.EndConnectionSite,False)'False=EndEndIfEndIfEndIfNext'シェイプタイプを切り替える。
    TargetShape.AutoShapeType = T
   'このとき、コネクタの接続が外れる'一覧に登録されたコネクタをTargetShapeに再接続するDim c AsVariantForEach c In processConnectors
        Dim processConnector As Shape: Set processConnector = c(0)If c(2)Then'True=Begin, False=End
            processConnector.ConnectorFormat.BeginConnect TargetShape, c(1)Else
            processConnector.ConnectorFormat.EndConnect TargetShape, c(1)EndIfNextEndSub

赤丸をつけて数えてみると、該当するものが8個。
f:id:t-hom:20180809014517p:plain

初期値が1なので、1+8 = 9
循環的複雑度は9という結果になった。
10未満なのでこのプロシージャの複雑度は、まずまず良好と言える。

参考

複雑度の指標
szk-takanori.hatenablog.com

複雑度の計算方法
saturday-dory-fever.blogspot.com


ちなみにRubberduckというVBEのアドインを使うと、循環的複雑度(Cycromatic Complexity)を自動的に計測してくれる。
Rubberduckは導入するとVBエディタの初回起動がもたつくのと、そもそもPCスペックがそれなりにないと重たいのと、すべて英語なのと、設計思想が本業プログラマー寄りなので、万人にオススメできるものではない。
でもとても便利なツールなので興味があれば導入してみると良いかもしれない。


Viewing all articles
Browse latest Browse all 493

Trending Articles