VBA で small is beautiful. する話【修正版】
※前の記事はコードの色表示が上手くいかなかったので削除しました。
( ´_ゝ`)ノシ
yoshitiaです。
前の現場ではbashとSQLでしたが、
新しい現場では VBA 、 VBscript 、コマンドプロンプトとMSづくしです。
そんな現場ですが Unix哲学が役にたたないわけではないです。
今回の記事では Unix哲学の1番目に出てくる
small is beautiful. (小さいことはいいことだ)
コードの単位を細かく細かくすることで
扱いやすいプログラムにする考え方です。
これをVBAで実践する例を書いていこうと思います。
https://www.amazon.co.jp/dp/4274064069/ref=cm_sw_r_tw_dp_x_M983xbVA8A8SXwww.amazon.co.jp
環境
Excel2007以降のxlsmファイルに標準モジュール作成
標準モジュール上でコードを書く。
small is beautiful. 実践前のコード
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値 = ワークシート.Range("A1").Value If セルからの取得値 = "" Then MsgBox "A1セルが空です" Else MsgBox "A1セル空欄チェックOK" End If End Sub
sheet1 の A1セルの値が空かどうかチェックする簡単なコードです。
例としてはまだ処理が少ないので読めますが
後から読む人にとっては何の処理をしているか
すぐにはつかみにくいです。
これを書き換えてみます。
small is beautiful. 実践後のコード
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値1 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1) End Sub Function セルチェック1(セルの場所 As String, セルからの取得値 As String) As String セルチェック = 空欄チェック(セルの場所, セルからの取得値) End Function Function 空欄チェック(セルの場所 As String, セルからの取得値 As String) As String If セルからの取得値 = "" Then 空欄チェック = セルの場所 & " が空です" Exit Function End If 空欄チェック = セルの場所 & " 空欄チェックOK" End Function
実践後のコードです。
人によっては「余計に複雑になった」と思う人もいるかもしれません。
ここで、メインの ワークシートチェック() だけを見てみます。
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値1 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1) End Sub
ここからさらに見る範囲を絞ります。
Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1)
Excelを扱ったことのある人なら
「sheet1 の A1セルに入力されている値をチェックしている」
となんとなくは読み取れると思います。
では、最初のコードではどうでしょう。
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値 = ワークシート.Range("A1").Value If セルからの取得値 = "" Then MsgBox "A1セルが空です" Else MsgBox "A1セル空欄チェックOK" End If End Sub
このコードだと If文以降を全部読んで確認しないと
A1セルをチェックしている処理だと把握できません。
コードを小さくする時の考え方
コードの何を小さくしているかの話です。
具体的には処理の内容を分割しています。
コード全体の処理内容は
「sheet1 の A1セルの値が空かどうかチェックする」です。
これを次のような単位に分けます。
- 「セルの値をチェック」
- 「セルが空かどうかチェック」
「セルが空かどうかチェック」の部分は
「セルの値をチェック」の中に入っています。
利用する人は、「セルの値をチェック」に
対象のセルの値をセットするだけで使えます。
利点その1. 再利用しやすい
コピペで使いやすいです。
例えば、追加で「A2セルもA1セルと同じチェックをしたい」場合、
変数宣言に```Dim セルからの取得値2 As String```を追加
値取得に```セルからの取得値2 = ワークシート.Range("A2").Value```を追加
ここまでは実践前・実践後両方同じですが
処理を追加する場合、
small is beautiful. 実践前
If セルからの取得値2 = "" Then MsgBox "A2セルが空です" Else MsgBox "A2セル空欄チェックOK" End If
small is beautiful. 実践後
MsgBox セルチェック1("A2", セルからの取得値2)
追加する量が変わってきます。
チェックするセルの数が増えるほど差が出ます。
利点その2. 機能追加しやすい
ただ、これだけでは
Function セルチェック1 と Function 空欄チェック
この2つに分けた利点が分かりにくいと思います。
私も今までの説明だけなら
Function セルチェック1 に Function 空欄チェック のコードも
まとめればいいと思います。
しかし、このように分けているのも理由があります。
例えば、「A1セルの値が"1"かどうかのチェックも追加したい」
となった場合、Function セルチェック1にまとめてしまっていると
融通が効きません。
Function セルチェック1 と Function 空欄チェック のように分けていると
A1セルの処理
- 「セルの値をチェック」
- 「セルが空かどうかチェック」
- 「セルの値が1かどうかチェック」
A2セルの処理
- 「セルの値をチェック」
- 「セルが空かどうかチェック」
のように対象のセルごとにチェックの種類を
楽々追加/削除ができるようになります。
実際のコードは以下のとおりになります。
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値1 As String Dim セルからの取得値2 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1) セルからの取得値2 = ワークシート.Range("A2").Value MsgBox セルチェック2("A2", セルからの取得値1) End Sub Function セルチェック1(セルの場所 As String, セルからの取得値 As String) As String Dim チェックする値 As String チェックする値 = "1" セルチェック = 空欄チェック(セルの場所, セルからの取得値) セルチェック = セルチェック & vbCrLf & 値チェック(セルの場所, セルからの取得値, チェックする値) End Function Function セルチェック2(セルの場所 As String, セルからの取得値 As String) As String セルチェック = 空欄チェック(セルの場所, セルからの取得値) End Function Function 空欄チェック(セルの場所 As String, セルからの取得値 As String) As String If セルからの取得値 = "" Then 空欄チェック = セルの場所 & " が空です。" Exit Function End If 空欄チェック = セルの場所 & " 空欄チェックOK。" End Function Function 値チェック(セルの場所 As String, セルからの取得値 As String, チェックする値 As String) As String If セルからの取得値 <> "1" Then 空欄チェック = セルの場所 & "の値が " & チェックする値 " ではありません。" Exit Function End If 値チェック = "チェックOK。 " & セルの場所 & " の値は " & セルからの取得値 & " です。" End Function
コードの量は増えましたが
ワークシートチェック()の部分だけを見てみると
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値1 As String Dim セルからの取得値2 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1) セルからの取得値2 = ワークシート.Range("A2").Value MsgBox セルチェック2("A2", セルからの取得値1) End Sub
追加前と比較して
Dim セルからの取得値2 As String
セルからの取得値2 = ワークシート.Range("A2").Value
MsgBox セルチェック2("A2", セルからの取得値1)
の追加だけで済んでいます。
初めてこのコードを見る人でも
「sheet1 の A1セル と A2セル のチェック処理」
と概要を把握しやすいです。
まとめ
最後に、Unix哲学を元にコードを書いている時の私のイメージは
ゲームで1000回攻撃しないと倒せない相手を、
必殺技などを使わずに
愚直に連続攻撃を、隙間なく、1000回叩きこんで倒すイメージです。
(実際のゲームでは1000回は不要でしょうが)
https://www.amazon.co.jp/dp/4274064069/ref=cm_sw_r_tw_dp_x_M983xbVA8A8SXwww.amazon.co.jp
『UNIXという考え方』でもゴジラを題材に
Unix哲学を元に難問に立ち向かう開発者のイメージが語られています。
例として示したコードはまだまだ使い勝手が良くないです。
もっと良いコードの書き方はあるはずです。探しましょう。
Unix哲学についてはsmall is beautiful. 以外にもありますので
webの記事や書籍『UNIXという考え方』で見ると良いと思います。