栄養計算ソフトにユーザーフォームを導入しよう!第6回:コードを改善しよう

「栄養計算ソフトにユーザーフォームを導入しよう」の連載第6回です。

以下の記事の続きになります。

栄養計算ソフトにユーザーフォームを導入しよう!第5回:食品入力をスムーズにしよう
「栄養計算ソフトにユーザーフォームを導入しよう」の連載第5回です。 以下の記事の続きになります。 前回は、リストボックスに食品群ごとに食品を表示する処理を実装しました。今回は、食品をシートに追加する際の処理を見直し、もっ

前回は、食品入力時の問題点を列挙し、それに対する改善策をご紹介しました。今回は、コードの可読性を高め、またメンテナンスもしやすいようにコードを改善していきたいと思います。

リーダブルコードのメリットは?

読みやすく、保守性の高いコードのことを「リーダブルコード」といいます。今回は、今までに書いてきたコードを整理し、リーダブルコードになるように改善していきます。そこでまずは、リーダブルコードのメリットを紹介します。

変更にも柔軟に対応できる

リーダブルコードを書くことで、急な変更などにも柔軟に対応することができます。

たとえば消費税率です。消費税率をコードの中に使用していると、消費税率が変更になるたびにその部分を見つけ出し、修正する必要があります。その際に、リーダブルコードで書いていれば修正もすぐに済むでしょう。消費税率を定数化しておけば、それを変更するだけで済む話です。

また、元号なども、消費税率と同様に、変更のたびにメンテナンスが必要となる項目です。しかしこちらも、リーダブルコードで書いていると、修正の手間が少なくて済むようになります。

コードを使いまわせる

また、リーダブルコードで書くことで、コードを使い回せるようになります。

コードを書いた自分自身や他人が、そのコードを簡単に読めると、初めからそれを書く必要はなくなりますよね。同じ処理をしているのであれば、始めから書くよりも、すでに書いたコードを使いまわしたほうが手間は少なくなります。

つまり、開発に伴う速度が早くなります。すでに書いたコードはデバッグの必要性が少ない場合もあるので、その点でもメリットが多くなります。

バグの原因を特定しやすい

リーダブルコードで書くことで、バグの原因を特定しやすくなります。

これは、読みやすいコードを書くことで、バグの原因となる部分が明白になるためです。読みにくく、整理されていないコードがずらずらと書かれていたら、当然、バグの原因となる部分は見つけにくくなります。

開発を効率的にバグを発見しやすくするためにもリーダブルコードは必要なのです。

 

実際に改善していこう

では、実際にコードをリーダブルとすべく、改善していきましょう。

数値や文字列を定数化しよう

手始めに、数値や文字列を定数化していきましょう。

今回書いたコードでは、数値が、それが何を示すのかがパット見でわからないものがあります。たとえば以下の部分。

Private Sub UserForm_Initialize()
    Dim i As Long
    For i = 1 To 18
         Controls("OptionButton" & i).Caption = Worksheets("設定").Cells(i, 1).Value
    Next i
End Sub

ここでは、For~Nextステートメントを1から18までループさせています。しかし、その1と18が何を示しているのかが分かりづらいです。そのため、こういった部分を定数化していきます。

今回使用する定数は、他のフォームなどでも使用します。そのため、Public定数として、他のモジュールなどからでも使用できるようにします。「挿入」→「標準モジュール」の順にクリックし、「Module1」の先頭に以下のコードを記述してください。

Public Const FG_START_INDEX As Long = 1 '食品群の開始番号
Public Const FG_END_INDEX As Long = 18 '食品群の終了番号
Public Const NCS_START_ROW As Long = 5 '栄養計算シートの開始行番号
Public Const NCS_FOODNUM_CLM As Long = 2 '栄養計算シートの食品番号の列番号
Public Const NCS_FOODNAME_CLM As Long = 3 '栄養計算シートの食品番号の列番号
Public Const MAIN_START_ROW As Long = 9 '本表シートの食品の開始行
Public Const MAIM_FOODGROUP_CLM As Long = 1  '本表シートの食品群の列番号
Public Const MAIM_FOODNUM_CLM As Long = 2  '本表シートの食品番号の列番号
Public Const MAIN_FOODNAME_CLM As Long = 4  '本表シートの食品名の列番号
Public Const WSNAME_MAIN As String = "本表" '本表のシート名
Public Const WSNAME_SUB As String = "別表"  '別表のシート名
Public Const WSNAME_NCS As String = "栄養計算シート" '栄養計算シートのシート名
Public Const WSNAME_STG As String = "設定"  '設定シートのシート名

意味が分かりづらくなっている数値や文字列を定数として宣言しました。そのほか、シート名も定数としました。こうすることで、もしシート名を変更したくなったという場合でも、定数の部分を変更することでそれを達成できるようになります。

では、これらの定数を使用して、現在のコードを書き換えて行きましょう。

まずはフォーム開始時の処理から

Private Sub UserForm_Initialize()
    Dim i As Long
    For i = FG_START_INDEX To FG_END_INDEX
         Controls("OptionButton" & i).Caption = Worksheets(WSNAME_STG).Cells(i, 1).Value
    Next i
End Sub

 

次は反映ボタンを押した際の処理について。

Private Sub btnRef_Click()
    Dim i As Long
    Dim fg As Long
    For i = FG_START_INDEX To FG_END_INDEX
        If Controls("OptionButton" & i).Value = True Then
            fg = Val(Left(Controls("OptionButton" & i).Caption, 2))
        End If
    Next i
    lstFood.Clear
    Dim mainLastRow As Long
    mainLastRow = Worksheets(WSNAME_MAIN).Cells(Rows.Count, 1).End(xlUp).Row
    For i = MAIN_START_ROW To mainLastRow
        If Worksheets(WSNAME_MAIN).Cells(i, 1).Value = fg Then
            lstFood.AddItem (Format(Worksheets(WSNAME_MAIN).Cells(i, MAIM_FOODNUM_CLM).Value, "00000") & " " & Worksheets(WSNAME_MAIN).Cells(i, MAIN_FOODNAME_CLM).Value)
        End If
    Next i
End Sub

 

次は追加ボタンを押した際の処理について

Private Sub btnAdd_Click()
    If ActiveCell.Row < NCS_START_ROW Then
        MsgBox "項目行より下を選択してください", vbCritical, "注意"
        Exit Sub
    End If
    Dim foodNum As String
    foodNum = Left(lstFood.Value, 5)
    Cells(ActiveCell.Row, NCS_FOODNUM_CLM).Value = foodNum
    ActiveCell.Offset(1, 0).Activate
End Sub

今回変更した部分はハイライトされています。

特段、説明の必要はないかもしれませんが、反映ボタンを押した際の処理の10-11行目にmainLastRowという変数を宣言し、値を格納しています。これは、本表シートの最終行を示す行番号を示しています。

mainLastRow = Worksheets(WSNAME_MAIN).Cells(Rows.Count, 1).End(xlUp).Row

では、本表シートのセル:一番下の行の1列目から上に移動し、値があった場所の行番号をmainLastRowに格納しています。

Rows.Countというのは、エクセルのアプリケーション上の行の数、つまりエクセルシートを一番下まで移動した際の行番号を示しています。1048576がそれですね。

そこから、End(xlUp)で上方向に移動しています。Ctrl+↑を押した際と同じ動きです。

そして、その場所の行番号を取得しています。

こうすることで、本表シートに食品を追加したりした際に、最終行が変更になったとしても、対応することができます。これが、メンテナンスしやすい、変更に強いコードですね。

 

コメントを追加しよう

読みやすいコードとすべく、コメントを追加していきましょう。

特に、今回定数化しなかったけど、任意の数字が使われている部分なんかは、時間を置いて後から読み直した際に、すぐに意味がわからないかもしれません。なので、そういった部分にコメントを付けておきます。

Private Sub btnAdd_Click()
    If ActiveCell.Row < NCS_START_ROW Then
        MsgBox "項目行より下を選択してください", vbCritical, "注意"
        Exit Sub
    End If
    Dim foodNum As String
    foodNum = Left(lstFood.Value, 5) '左から5文字が食品番号
    Cells(ActiveCell.Row, NCS_FOODNUM_CLM).Value = foodNum
    ActiveCell.Offset(1, 0).Activate
End Sub

 

Private Sub btnRef_Click()
    Dim i As Long
    Dim fg As Long
    For i = FG_START_INDEX To FG_END_INDEX
        If Controls("OptionButton" & i).Value = True Then
            fg = Val(Left(Controls("OptionButton" & i).Caption, 2))  'オプションボタンの先頭2文字が食品番号。それを取得し数値に変更 例)01→1など
        End If
    Next i
    lstFood.Clear
    Dim mainLastRow As Long
    mainLastRow = Worksheets(WSNAME_MAIN).Cells(Rows.Count, 1).End(xlUp).Row
    For i = MAIN_START_ROW To mainLastRow
        If Worksheets(WSNAME_MAIN).Cells(i, 1).Value = fg Then
            lstFood.AddItem (Format(Worksheets(WSNAME_MAIN).Cells(i, MAIM_FOODNUM_CLM).Value, "00000") & " " & Worksheets(WSNAME_MAIN).Cells(i, MAIN_FOODNAME_CLM).Value)
        End If
    Next i
End Sub

 

Withステートメントで処理をまとめよう

最後は、Withステートメントを用いて、コードを美しくまとめます。

以下のように変更すればOKです。

Private Sub btnRef_Click()
    Dim i As Long
    Dim fg As Long
    For i = FG_START_INDEX To FG_END_INDEX
        If Controls("OptionButton" & i).Value = True Then
            fg = Val(Left(Controls("OptionButton" & i).Caption, 2))  'オプションボタンの先頭2文字が食品番号。それを取得し数値に変更 例)01→1など
        End If
    Next i
    lstFood.Clear
    Dim mainLastRow As Long
    mainLastRow = Worksheets(WSNAME_MAIN).Cells(Rows.Count, 1).End(xlUp).Row
    For i = MAIN_START_ROW To mainLastRow
        With Worksheets(WSNAME_MAIN)
            If .Cells(i, 1).Value = fg Then
                lstFood.AddItem (Format(.Cells(i, MAIM_FOODNUM_CLM).Value, "00000") & " " & .Cells(i, MAIN_FOODNAME_CLM).Value)
            End If
        End With
    Next i
End Sub

 

まとめ

今回はコードを読みやすく、かつメンテナンスもしやすいようにコードに改善を加えました。まだまだ改善できる点はありますが、今のところはこれでよいでしょう。次回は、検索して食品を入力するフォームの処理を記述していきます

 

連載目次