ゆるおたノート

Tomorrow is another day.

【VBA】自分用コーディングガイドライン

自分用のツールだけでなくお仕事としてVBAを書くことが増えてきたので、自分用のコーディングガイドラインを作ってみました。

勉強中のため、全部は固まっておらずところどころ説明が空欄だったり例が適当だったりしますが、「今のところ」の話として公開します。
今後も随時更新予定です。

このページについて

想定読者

  • 最近は作ったツールを誰かに渡すことが多いため、その点を念頭に置いて作成しています。
    作成者自身で使用する場合や、熟練者同士のコーディングなどにはそぐわない部分もあるかもしれません。あらかじめご了承ください。

  • 他の誰かが作ったものを改修する場合は、当ページの内容より作業中のルールを優先することとします。

優先度

各項目の【】の部分は、下記の順に優先としています。

優先度 意味
MUST 必ず守る
BETTER 迷ったらこっち。絶対ではない。
WISH 検討中・勉強中。

クックパッド社のコーディング規約を参考にさせて頂きました()。

ただ、まだコーディングに自信があるわけではないのでSHOULD(~すべき)ではなくBETTER(~した方が良い)としています。笑

考え中のこと、疑問点など

勉強中の身なので、まだ自分の中で確立できていないこともあります。

そのような部分には、🤔(考えていること)としてコメントを記載します。

全体

【MUST】プロシージャは1画面に収める

特別な理由がない限り、80字程度×20~30行程度までにする。

  • 1行が長くなる場合は、改行(_)を積極的に使用する。
    (並行して、コードの縦のラインが揃うように必ずレイアウトに気を使うこと。)
  • 処理の分岐等でこれを超える場合は、プロシージャの切り出しを検討する。

【MUST】省略せずに明記する

  • 省略形は極力使わず、単語を明記する。
    なお、プログラミング用語(特にVBA)として普及していて、運用担当者が一瞥で誤読なく識別できるものであれば、使用しても良い。

  • ビジネス用語などの所謂「横文字」の言葉も、可能な限り別の表現に言い換える。
    (可読性が運用担当者のバックグラウンドに左右されやすいため。)

  • インテリセンス(入力補完)を積極的に使用することとし、一般に普及している語句であれば~ation系の長い単語も使用可とする。

【BETTER】結論ファースト

  • 結果として出力したいものを最初に書き、段落(コードブロック)の要約の意味も持たせる。

    • 目的の変数
    • 補助的な変数
    • 補助的な変数…
    • 中間処理
    • 中間処理
    • 中間処理…
    • 結果
Dim shAbc As Worksheet             '目的の変数
Dim baseBook as Workbook           '補助的な変数(一瞬しか使わないもの)
set baseBook = ThisWorkbook        '中間処理
set shAbc = baseBook.Worksheets("シート名") '結果
'メイン処理 ─────────────────
Sub Main()
    Call SubProcess1
    Call SubProcess2
End Sub

'中間処理1 ─────────────────
Sub SubProcess1()

    Dim 変数 As 型
    変数 = 中間の中間1

    Call 中間の中間2

End Sub

Private Function 中間の中間1()As '処理
End Function

Private Sub 中間の中間2()
    '処理
End Sub

'中間処理2 ─────────────────
Sub SubProcess2()
    '処理
End Sub

【BETTER】それ自体に意味を持たない単語を避ける

(日本人が)イディオムとして認識しやすい言葉を除き、その単語自体で意味を判別できない単語は、可能な限り使用しないようにする。

  • 動詞

    • do
    • make
    • take
    • have
    • set
    • get
  • 形容詞・名詞

    • target

【BETTER】数値の表現

単語 使いどころ
Count 数え上げる時
Number ~の番号、数値の計算

🤔(Number of ~はどうする?)

【BETTER】前置詞

  • 識別子1つあたり1つまでとする。
    A of Bは、B Aと表現できる(ことが多い)ので、言い換えが出来ないか検討する。

  • プロシージャやメソッド名などの末尾に前置詞を置くと、英文として読みやすくなる(気がする)。

コメント

コメント

【MUST】読めば分かることは書かない
  • コードは主に英語を使用するが、コメントが「ただの翻訳」になってしまう時は、そもそも変数名やプロシージャ名を変更した方がスマートになることが多い。

  • 数カ月後の自分が「コードを読むだけで処理の内容を把握できるもの」を目指すこと。

【MUST】操作の意図を示す
  • 同じ結果を得るのに複数の選択肢がある場合や、何らかの処理を飛ばすような場合には、「なぜそうしたのか」を残すこと。
    (後で考慮漏れを検証しやすくするため)
【BETTER】要約コメント
  • 複数行に渡る処理には、段落分けの意味で要約コメントを付与してもよい。
    (併せて処理の区切りごとに空行も必ず挟むこと。)
【BETTER】エラーの可能性の示唆
  • 分かる範囲で。

  • 「何が起こるとエラーになるのか」を記す。

  • 資料として、ベン図やパターンの列挙があると尚良し。
    (後で考慮漏れを検証しやすくするため)

ドキュメンテーション・コメント

【MUST】プロシージャごとに記載する
  • 再利用しやすくするため、小分けしたプロシージャごとにコメントを記載していく。

🤔(全部に書くのは冗長かな…?)

【MUST】書式

下記の書式とする(不要なものは削除)。

''
' ●概要●
'
' 【参考】
' ●記事名● | ●サイト名●
' ●URL●
'
' 【参照設定】
' [Name] ●●●●
' [note] ●●●●●●●●●●●●
'
' @param {型} ●name● ●内容●
' @param {型} ●name● ●内容●
' @param {型} ●name● ●内容●
'
' @return {型} ●内容●
'
' @customfunction ●内容●
'

(記号が多いのは書き忘れ・消し忘れを防ぐため。)

【BETTER】必要なデータの例示
  • 「何」が「どうなっている」と「どう出るのか」を例として記す。
    (後で考慮漏れを検証しやすくするため)
【BETTER】エラーの可能性の示唆
  • 分かる範囲で。

  • 前項に関連して、「何が起こるとエラーになるのか」を記す。

  • ベン図やパターンの列挙があると尚良し。
    (後で考慮漏れを検証しやすくするため)

【BETTER】5W1Hの明記

曖昧な表現になりそうな時は、省略せずすべて記載する。

5W1H 意味
When いつ
Where どこで
What 何を
Who/Whom 誰が/誰に
Why 何のために
How どのように

プロジェクト

【WISH】単一プロジェクトのプロジェクト名

🤔(VBAProjectのままで良い?)

【WISH】複数プロジェクトのプロジェクト名

(勉強中)

モジュール

全体

【MUST】記法

シートモジュール(後述)を除き、パスカル記法とする。

    • OpenWorkbook
    • ImportRawData
    • SheetFormatter

標準モジュール

【WISH】オブジェクト名

(考え中)

ブックモジュール

【MUST】単一プロジェクトのオブジェクト名
  • ThisWorkbookのままとする。
【BETTER】複数プロジェクトのオブジェクト名

(勉強中)

シートモジュール

【MUST】オブジェクト名
  • sh<シート名>とする。
    ※オブジェクトブラウザでのフォルム統一のため、接頭辞をshとする。

クラスモジュール

【MUST】オブジェクト名
  • 名詞形とする。
    ~er~istなど、「~するもの」といった表現だと尚良し。

ユーザーフォーム

【WISH】オブジェクト名

(勉強中)

プロシージャ

全体

【MUST】記法

すべて「キャメルケース記法」とし、スコープにより1文字目を変える。

スコープ 1文字目
パブリック 大文字 OpenBook
プライベート 小文字 sumPopulation
【MUST】求める結果 > 処理の内容

実際の処理はプロシージャの中に閉じ込めることにして、プロシージャ名は「どんな結果になるか」が読み取れる名前を優先する。

【MUST】ネストは3つまで
  • WithブロックSelect Case文を含めて、ネストは3つまでとする。

  • ネスト1階層あたり1段のインデントとする。

  • ネストが深くなる時は、関数化や早期リターン、ガード節を積極的に使用すること。

【MUST】早期リターン/ガード節の推奨

n重ループや処理の分岐などは、If文と組み合わせて改善すること。

  • 複数条件
Sub improveIfNests()

    If isAbc() Then Exit Sub
    If Not d = e Then Exit Sub
    If f = g Then Exit Sub

    'すべてFalseのときの処理

End Sub
  • ループのスキップ
Sub improveForNestsBySkip()

    Dim i As Long
    For i = 0 to Ubound(array1d)
        If array1d(i) = 1 Then goto Continue

        '繰り返し処理

Continue:
    Next i

End Sub
  • ループの抜け出し
Sub improveForNestsByExitFor()

    Dim i As Long
    For i = 0 to Ubound(array1d)
        If array1d(i) = 1 Then Exit For

            '繰り返し処理

    Next i

End Sub
  • n重ループの抜け出し
Sub improveForNestsByBreak()

    Dim cell As Variant
    Dim i As Long
    For Each cell In searchRange
        For i = 0 to Ubound(array1d)
            If array1d(i) = 1 Then goto Break

        Next i
    Next cell

Break:
    '次の処理

End Sub
【BETTER】「~にする」という表現

make ~などの表現は、1語で言い換えられないか検討する

  • 参考

    接頭辞・接尾辞 意味 使いどき
    ~ize <名詞>化する、<名詞>にする
    ~fy <名詞>化する、<名詞>にする
    en~ より<形容詞>にする
    make~ ~にする 上記どれも一般的な単語がない場合
【BETTER】「~を行う」という表現

do ~take ~などの表現は、1語で言い換えられないか検討する

【BETTER】補助関数
  • スニペットなどに登録する際は、抽象的な名前も可とする。
  • 実際にコード中でヘルパー関数として使用する時は、「何がどう変わるのか」を(可能な限り)明示した名前に変更する

Subプロシージャ、Functionプロシージャ

【MUST】命名

動詞始まりの1~5語程度の語句とする。

  • 文型

    • V
    • VO
    • VOO
    • VOC
  • GAS(ないしJavaScript)のメソッド名も参考とする。
    (英文として平易な表現が多い(気がする)ため。)

【MUST】条件を判定する関数
  • 原則、下記の命名規則とする。

    • is~
    • can~
    • has~
  • If文で呼び出すときに肯定文となるように書く。

If is確認すること()[ = True] Then

🤔(中止の条件判定の場合は、否定形の形容詞の方が自然かも?)

  • bad
Sub Example()
    If checkPathWhetherValid() = False Then Exit Sub

    '処理

End Sub

Private Function checkPathWhetherValid() As Boolean

    '条件の判定処理

End Function
  • good
Sub Example()
    If isInvalidPath(sourcePath) Then Exit Sub

    '処理

End Sub

Private Function isInvalidPath(ByVal sourcePath As String) As Boolean

    '条件の判定処理

End Function

プロパティプロシージャ

【MUST】命名

名詞句とする。

【BETTER】プロパティの接頭辞を統一する
  • 接頭辞

    接頭辞 命名 意味
    p pプロパティ名 プライベートレベルのモジュール変数
    a aプロパティ名 プロパティプロシージャの引数
    なし プロパティ名 プロパティ名
  • 名前を揃えることでプロシージャごとの置き換えがしやすくなり、フォルムも揃えやすい(はず)

Private pPropName AsProperty Get PropName(ByVal arg1 As, _
                      ByVal arg2 As) As 型

    pPropName = PropName
    PropName = pPropName

End Property

Property Let PropName(ByVal arg1 As, _
                      ByVal arg2 As, _
                      ByVal 代入値 As)

    pPropName = PropName
    PropName = pPropName

End Property

変数/定数

全体

【MUST】記法
  • 定数のみ「スネークケース記法」とし、それ以外はすべて「キャメルケース記法」とする。
    なお、VBAでは「ハイフン-」を識別子として使用できない。*1

      • 定数

        • MAX_SHEET_COUNTS
        • AUTHOR_NAME
      • それ以外

        • abcBookName
        • stopTime
  • スコープにより1文字目を変える

    スコープ 1文字目
    パブリック 大文字
    プライベート 小文字
【MUST】数値の型を統一する
数値
整数 Long型
小数 Currency型
【MUST】変数名は3語まで
  • 可能であれば2語。
  • 動名詞~ing、動詞の名詞形~ationなどを積極的に使用すること。
【BETTER】型は変数名ではなく宣言に任せる
  • メソッド、関数で使用するものなど、文脈で型が分かるものは型名を変数名に付与しなくても良い。

  • not worse

Dim objAbcBook As Workbook
  • better
Dim abcBook As Workbook
【BETTER】似たものを対で扱う時は、名前やフォーマットを寄せる
  • 対になる変数を扱う時や型によって操作を分けるような時は、型名を明示した方が読みやすくなることもある。

  • この場合は、コードのフォルムを整えられるように(可能な限り)文字数を統一すること。
    (一瞥で差異を見つけやすくなるため)

  • not worse

Dim sourcePath As String
Dim destinationPath As String
  • better
Dim srcPath As String
Dim dstPath As String
  • not worse

    🤔(例が微妙…)

Dim wbAbcBook As Workbook
Dim nameAbcBook As String
Dim pathAbcBook As String
  • better
Dim abcBookObj As Workbook
Dim abcBookName As String
Dim abcBookPath As String

変数/定数

【BETTER】配列やコレクションの命名
  • 原則、複数形を使用する。

  • 単数形と複数形の区別がない単語や、複数形にするとかえって読みづらい語句の場合は、~Array~Collectionも可とする。

オブジェクトの定義・宣言

【BETTER】固有オブジェクト型の推奨
  • Object型ではなく、各オブジェクト固有の型で宣言すること。
    (インテリセンスが適用され、コーディングのスピード、可読性ともに向上するため。)

    • Workbook
    • Worksheet
    • 独自クラス、など
【WISH】参照設定したライブラリのオブジェクトを使用する時は、条件付きコンパイルを添える
  • ユーザーの利用環境が挙動に影響する場合にマクロが使えなくなってしまうことを防ぐ。
    ExcelからOutlookを操作する場合など)

列挙体

「変数・定数の中に入れるべきか」というのはあるけど、用途が似ているので…

【MUST】型名の接尾辞はeとする
【BETTER】メンバーは大文字始まりとする

🤔(たまに変数と被るのがちょっと…)
🤔(メンバーにも接頭辞つけたらうるさい…?)

【BETTER】引き数の選択肢として列挙体を使う
  • モードの切替など、プロシージャの引き数の選択肢として使うと、インテリセンスが使えるようになり少しだけコーディングが楽になる。

  • 注意

    • 列挙体は宣言セクションにしか宣言できずプロシージャと距離が離れてしまうことが多いので、出来るだけ2~3個までに抑える。

    • クラスモジュールなどで使う時はパブリックレベルでないといけないので、名前がバッティングしないように確認しながら使用する。

Public Enum eBookType
    InputBook
    SharedBook
    TemplateBook
End Enum

Sub Main()

    Call openBook(eBookType.InputBook)

End Sub


Private Sub openBook(ByVal bookType As eBookType)

    Dim book As Workbook
    Dim bookPath As String
    bookPath = "c:\\abc.xlsx"
    Select Case bookType
        Case eBookType.InputBook
            Set book = Workbooks.Open Filename:=bookPath, _
                                      ReadOnly:=False, _
                                      IgnoreReadOnlyRecommended:=True

        Case eBookType.SharedBook
            Set book = Workbooks.Open Filename:=bookPath, _
                                      ReadOnly:=True, _
                                      IgnoreReadOnlyRecommended:=False

        Case eBookType.TemplateBook
            bookPath = "c:\\abc.xltx"
            Set book = Workbooks.Open Filename:=bookPath, _
                                      Editable:=True, _
                                      ReadOnly:=False, _
                                      IgnoreReadOnlyRecommended:=True
    End Select

End Sub

構造体

「変数・定数の中に入れるべきか」というのはあるけど(ry

【MUST】型名の接尾辞はtとする
【BETTER】メンバーは大文字始まりとする

🤔(たまに変数と被るのがちょっと…)
🤔(メンバーにも接頭辞つけたらうるさい…?)

参考

以下を参考にさせて頂きました。

記事・サイト

書籍

*1:「ケバブ形式」なる記法があるらしい(美味しそう…)