和風スパゲティのレシピ

日本語でコーディングするExcelVBA

Elseの中にメインコードを書かない

読みやすいコードに関する小ネタです。


とても単純な話なので、単刀直入に。

Sub なにかのマクロ()

    If メインコードを始めてはいけない条件式 Then
        Msgbox("○○なため、処理を中断しました。")
    Else

        メインの処理50行
        ↓
        ↓

    End If

End Sub

↑こう書いている方いらっしゃいましたら、

↓こう書けますからこっちで書きましょう。

Sub なにかのマクロ()

    If メインコードを始めてはいけない条件式 Then
        Msgbox("○○なため、処理を中断しました。")
        Exit Sub
    End If

    メインの処理50行
    ↓
    ↓

End Sub

というお話です。


メインコードのインデントが1つ減るのが直接的なメリットですが、

まあそういう実利の話以前に、
終わった話は終わったと明示しましょう
ってことですね。



私が初めてこの書き方を見た時、割と感動しました。

言われてみればなるほどですよね。


出来る人は当たり前のようにやってると思います。
他言語のプログラマさんには当たり前すぎて、話題にもならないのでは?


しかし、独学で学んだVBAプログラマは、
他の人のコードを見る機会に恵まれていないため、
案外こういう「スキルとも呼べない程度のスキル」が抜け落ちます。


「身につける」なんてたいそうな話ではないので、今日からこっちで書きましょう。

開始条件がたくさんある場合はまとめて関数へ

さて、この「メインの処理を開始する前の、ちょっとしたIf文」ですが、
1個で済まず、たくさん並ぶことも結構あります。

Sub ユーザー指定のくだものをお取り寄せ()

    Dim くだものID As Variant
    くだものID = Range("D4") ' ユーザー入力セル

    If くだものID = "" Then
        MsgBox ("くだものIDを入力してください。")
    
    ElseIf IsNumeric(くだものID) = False Then
        MsgBox ("くだものIDは数値で入力してください。")
        
    ElseIf Getくだもの名(くだものID) = "" Then
        MsgBox ("入力されたIDにはくだものは登録されていません。")
        
    Else

        メインの処理50行
        ↓
        ↓

    End If

End Sub


これを見直すときは、
冒頭のやり方を抑えつつ、関数(Functionプロシージャ)に分割すると、
非常に読みやすくなります。

こんな感じです↓

Sub ユーザー指定のくだものをお取り寄せ()

    ' 開始条件を満たさなければExit
    If Isユーザーの入力がマクロの実行条件を満たす = False Then Exit Sub

    メインの処理50行
    ↓
    ↓

End Sub

' 開始条件判定
Function Isユーザーの入力がマクロの実行条件を満たす() As Boolean

    ' 実行条件を満たすかの判定は、スタート時はFalse
    Isユーザーの入力がマクロの実行条件を満たす = False

    Dim くだものID As Variant
    くだものID = Range("D4")

    ' 入力エラーをそれぞれ判定し、エラーがあればExit ⇒ ↑のままFalseが返る
    If くだものID = "" Then
        MsgBox ("くだものIDを入力してください。")
        Exit Function
    End If
    
    If IsNumeric(くだものID) = False Then
        MsgBox ("くだものIDは数値で入力してください。")
        Exit Function
    End If
    
    If Getくだもの名(くだものID) = "" Then
        MsgBox ("入力されたIDにはくだものは登録されていません。")
        Exit Function
    End If
    
    ' 最後まで突破出来たらTrue
    Isユーザーの入力がマクロの実行条件を満たす = True

End Function

 
このように、マクロの開始条件を判定しているIF文たちを、
最終的な合否を「True/False」で返す関数1個にまとめてしまいましょう。

メインマクロ上では数行(今回のようにExitするだけなら1行)になるので、
メインの処理が、すぐに見れるようになります。


逆にこれをやっていない場合は、
「一番大事なメインマクロが、たいして大事でない処理の羅列から始まる」
という構造になってしまっていたんですね。


メインマクロがすごく長いときは、
せめてこれをやるだけでもだいぶ読みやすくなります。

積極的に活用していきましょう。

おまけ:Boolean関数の名前の付け方

ちなみに今回の判定関数は「正常だったらTrue」でしたが、
これを逆にして「エラーがあったらTrue」にすることもできます。

関数内の一番上と一番下のTrue/Falseを逆にするだけですね。


この時はもちろん関数名も変えなければいけませんが、

If Isユーザーの入力がマクロの実行条件を満たす = False Then Exit Sub ' …① 正常ならTrue

If Isユーザーの入力に不備があれば警告を表示する Then Exit Sub ' …② エラーならTrue

こんな風に、設計と関数名が対応します。


どっちがいいですかね。


①の方が、「Exitします」感は出てますね。
でも②の方が、「MsgBoxがでてマクロが止まる」感が出てます。


私は「Enter押さないと完走しません」と明示したい気がするので、
②で書くことの方が多いかな?


まあこの辺は好みなので、どちらでもよいと思います。



が、好みと言いつつこれだけはダメです↓

If Isユーザーの入力エラーをチェック Then ' ←ど、どっち…?

 
これはやっちゃだめです。

  • 正常ならTrue
  • エラーがあればTrue

のどっちにも取れてしまいますからね。

真偽True/Falseを返す関数は、
「どっちの時にTrueになるか」がわかる関数名にしましょう。

おまけ:この関数は一番下へ

関数が完成しましたら、この関数はモジュールの一番下へ追いやり移動しましょう。

Sub ユーザー指定のくだものをお取り寄せ()

    If Isユーザーの入力に不備があれば警告を表示する Then Exit Sub
    
    メインの処理
    ↓
    Call 指定のくだものを仕入れる
    ↓
    Call 指定のくだものを梱包する(Get発送先(顧客ID))
    ↓
    ↓

End Sub

Private Sub 指定のくだものを仕入れる
End Sub

Private Sub 指定のくだものを梱包する
End Sub

Private Function Get発送先
End Function

Function Isユーザーの入力に不備があれば警告を表示する() As Boolean
End FUnction

こんなイメージで。


理由としては、
メインの関数と、そこで呼び出すサブの関数が近い方が読みやすいので、
重要じゃないこいつが間に挟まってると邪魔というのが一つ。


もう一つは、重要でないくせに改修が頻発するからです。


「その手があったか!」という意表を突いた操作をユーザーにされる度に、
この関数を書き換えることになりますので、一番下に配置しておきましょう。


ITは苦手と豪語おじさんから電話がかかってきたら、
Ctrl + End でこのプロシージャに移動できます。

お茶でも飲みながら、温かくお話を聞いてあげましょう。