■ 構造化の第一歩
VBAはその兄弟であるVisualBasic6.0と同様に手続き型言語です。と同時にオブジェクト指向型言語としての側面も持っています。
プログラミングの黎明期、初期のBASICやCOBOL等で書かれていたプログラムは非構造化プログラムでした。
これはコードが基本的に逐次実行型で構成されており、処理の分岐は指定された行ラベルにジャンプして行うなど、コードの可読性を低め保守性の悪いプログラムを生産する原因となっていました。
プログラムの規模が大きくなるにつれ、非構造化プログラミングは限界をむかえ、新たなるパラダイムとして生まれたのが構造化プログラミングです。
VBAは言語仕様そのものが既に構造化されており、意識しなくても構造化プログラムを記述することが可能です。
しかし意識して構造化することにより、さらなるプログラミングの省力化や再利用を高めることが可能になるでしょう。
構造化の第一歩はまとめることです。簡単にいえば同じコードをできるだけ2度書かないようにすることです。さて、test.xlsを開いてください。
今回は、前回のコードにチェック機能を追加することにしましょう。
チェックする項目は社員IDが「数値型であること、指定した桁数であること」にしましょう。
早速、プログラミングしてみます。
Public Sub test()
Dim i As Long
Dim WrkRange As Variant
If ChkNumber(Range("syain_id"), 4) Then
WrkRange = Sheets("SyainMST").UsedRange
For i = 1 To UBound(WrkRange)
If WrkRange(i, 1) = Range("syain_id") Then
Range("syain_id").Offset(1, 0) = WrkRange(i, 2)
Range("syain_id").Offset(2, 0) = WrkRange(i, 3)
Range("syain_id").Offset(3, 0) = WrkRange(i, 4)
Range("syain_id").Offset(4, 0) = WrkRange(i, 5)
Exit For
End If
Range("hyoji_all").ClearContents
Next i
Else
MsgBox "社員IDが有効ではありません", vbCritical
End If
End Sub
Private Function ChkNumber(ByVal pNumber As String, ByVal pKeta As Long) As Boolean
If IsNumeric(pNumber) Then
If Len(pNumber) = pKeta Then
ChkNumber = True
Exit Function
End If
End If
ChkNumber = False
End Function
こんな感じになります。ずいぶんコードが増えちゃいましたね。
まず、社員IDの有効チェックですが4行目の、
If ChkNumber(Range("syain_id"), 4) Then
のところで行っています。このChkNumberという関数は、私が勝手に作ったものですのでVBAのヘルプには当然載ってませんよ。念のため。
ChkNumberの文字中のどこでもいいですからカーソルを持ってきて、Shift+F2を押してください。
Function ChkNumber…のところに移動しましたね。VBEのどこでも、Shift+F2を押すことで関数のある場所にとんでいきます。戻るときはShift+Ctrl+F2を押せば戻ってきます。これはよく使うショートカットなので覚えておいてくださいね。
このとんでいった先のコードが社員IDのチェックを行っている関数ChkNumberです。
ChkNumberの(ByVal pNumber As String, ByVal pKeta As Long)の部分はどういう意味なんでしょう?この部分はChkNumber関数に必要な引数を定義しています。
それでは引数っていったい何?
チェックを行うために最低限必要なものはチェックする数値と桁数です。
ですのでこの2つのデータは必ずChkNumberに渡してやる必要があります。このとき渡すデータが引数です。
引数pNumberはチェックする数値そのものを指します(数値でない場合もあるので文字列型で渡しています)、引数pKetaはチェックする桁数を整数型で渡しています。
ChkNumberの頭にある Function と最後の As Boolean はどんな意味があるんでしょう?
Functionは返り値をもつプロシージャを意味します、As Booleanはその返り値の型を表しています。
関数には返り値を「もつ」もの(Functionプロシージャ)と「もたない」もの(Subプロシージャ)があるのです。
返り値ってなにかって?それは関数のコードを実行した結果を元の(呼び出した側の)コードに返す値です。
※引数、返り値の詳しい解説はこちらを参照してください
引数の参照方法(値渡し・参照渡し)を明示しよう
ChkNumberは指定桁数の有効な数値であるかどうかさえ分かればいいので、返り値でほしいのは"TRUE"(真)、"FALSE"(偽)のどちらかでいいわけです。
IsNumeric()関数で数値かどうかのチェックをしています。数値でなければここではじかれるのでChkNumberの返り値はFALSEです。
次のIf Len(pNumber) = pKeta Thenの部分はLen()関数でpNumberの桁数を調べます。その値がpKeta指定された桁数と異なればやはりはじかれてChkNumberの返り値はFALSEになります。そうでない場合、つまり数値チェックを通り、桁数も等しい場合だけ返り値にTRUEをセットされExit Functionで処理を終えます。
ですので呼び出し元のIf文でChkNumber(Range("syain_id"), 4)が"TRUE"ならば従来の処理が行われ、"FALSE"のときは社員IDが違うとメッセージが表示されます。
ちなみに呼び出し元のSub test()は返り値がありません。このプロシージャは実行してそれでおしまいなので返り値は必要ありませんね。
だからSubを使っているのです。(Subプロシージャでも引数を設定することは可能です。その場合は他のプロシージャより呼び出す必要があります)
ところであなた、構造化しているのにずいぶんコードが増えて複雑になったと思っていませんか?
この部分だけで見ればそうですが、今回作ったChkNumber関数。これは他のプロシージャからでも使うことができます。
たとえば郵便番号のチェックだとか電話番号のチェックなどにも使えます。
つまり再利用が可能なコードということです。
ということはChkNumber関数は一度書けば、二度と書く必要のないコードということになります。
立派に構造化されています。
このChkNumber関数を改編したい場合が生じても、この部分だけを直すことで全てのChkNumberを使用しているプロシージャに変更を反映できます。
もしこの部分を関数化せずにすべてのプロシージャ内で記述していたとしたら…当然改編時にはすべてのプロシージャの該当部分を変更する必要があります。
でもそれはナンセンスですし、修正漏れや改編ミスによるバグを引き起こす原因になります。
構造化プログラミングの第一歩は、できる限りコードの再利用化率を高め、保守性の良いプログラムを作りだすことにあります。
そのためにも汎用的な処理は関数化し、他コードからいつでも参照・利用できるように心がけてプログラミングしましょう。
|