[VBA] クラスを利用するメリットと方法について & 簡単なサンプル(2)

概要

この記事について

この記事は、
[VBA] クラスを利用するメリットと方法について & 簡単なサンプル(1)の続きの記事。
(1)とは違ったサンプルを用いて、前回とは違ったクラスの利用方法やそのメリットを記したい。

TableCreaterの例だと、カプセル化の恩恵や初期化処理・終了処理を必ず実行できることの恩恵が十分には表現できなかったように思えるため、
本記事のサンプル(clsCreateNewExcel)でそれらを表現しようと思う。

説明のために作成したAccessファイルとソースコードはこちらでダウンロードできます。

作成環境

Windows10
MSOffice 2019

サンプルの概要

Accessファイルで、内部に商品テーブルを持つ。

次のような画面から、
ボタンにより異なった処理を呼び出し実行する。

メインフォーム

いずれのボタンのイベントも、共通処理として、

  • デスクトップに新しいExcelブックを作成する(ファイル名:Test-Excel-Class-日付時刻.xlsx)

を持つ。

個別の処理として、

  • 商品テーブルから単価が10000を超える商品のデータを抽出し、
    作成したExcelブックのシートに転記する。
  • 商品テーブルからコードがBから始まる商品のデータを抽出し、
    作成したExcelブックのシートに転記する。
  • WebAPIより商品データのJsonデータを取得し、
    テーブル形式にパースしたのち、Excelブックのシートに転記する。

を持つ。

Excelブック作成とブック関連オブジェクトの管理のために、
clsCreateNewExcelクラス というクラスモジュールを作成した。

clsCreateNewExcelクラス

役割

clsCreateNewExcelクラスは新規Excelアプリケーションのインタフェースと新規Excelブックを生成し、
それぞれのオブジェクトを取得することを許す。
また、呼び出されるたびにブックにワークシートを追加する関数を持つ。

コード

  1'@Folder("class")
  2Option Compare Database
  3Option Explicit
  4
  5'**************************
  6'*Excelブック作成クラス
  7'**************************
  8
  9'定数
 10
 11'変数
 12Private currentSheetNum As Long
 13Private myXlApp As Object
 14Private myWorkBook As Object
 15Private dicWorkSheet As Dictionary
 16
 17
 18'******************************************************************************************
 19'*getter/setter
 20'******************************************************************************************
 21Public Property Get xlApplication() As Object
 22    Set xlApplication = myXlApp
 23End Property
 24
 25
 26Public Property Get Workbook() As Object
 27    Set Workbook = myWorkBook
 28End Property
 29
 30
 31Public Property Get WorkSheets(ByVal num As Long) As Object
 32    If Not dicWorkSheet.Exists(num) Then Call MsgBox("シートがありません。", vbExclamation, TOOL_NAME): Set WorkSheets = Nothing: Exit Property
 33    Set WorkSheets = dicWorkSheet.Item(num)
 34End Property
 35
 36
 37'******************************************************************************************
 38'*関数名    :Class_Initialize
 39'*機能      :
 40'*引数      :
 41'******************************************************************************************
 42Private Sub Class_Initialize()
 43    
 44    '定数
 45    Const FUNC_NAME As String = "Class_Initialize"
 46    
 47    '変数
 48    
 49    On Error GoTo ErrorHandler
 50    
 51    'シート数の設定
 52    currentSheetNum = 1
 53    
 54    'オブジェクトの設定
 55    Set myXlApp = CreateObject("Excel.Application")
 56    With myXlApp
 57        'バックグラウンドで生成・編集
 58        .Visible = False
 59        .ScreenUpdating = False
 60        .DisplayAlerts = False
 61    End With
 62    Set myWorkBook = myXlApp.Workbooks.Add
 63    Set dicWorkSheet = New Dictionary
 64    dicWorkSheet.Add currentSheetNum, myWorkBook.WorkSheets(currentSheetNum)
 65    
 66ExitHandler:
 67
 68    Exit Sub
 69    
 70ErrorHandler:
 71
 72    MsgBox "エラーが発生したため、マクロを終了します。" & _
 73           vbLf & _
 74           "関数名:" & FUNC_NAME & _
 75           vbLf & _
 76           "エラー番号:" & Err.Number & vbNewLine & _
 77           Err.Description, vbCritical, TOOL_NAME
 78        
 79    GoTo ExitHandler
 80        
 81End Sub
 82
 83
 84'******************************************************************************************
 85'*関数名    :Class_Terminate
 86'*機能      :
 87'*引数      :
 88'******************************************************************************************
 89Private Sub Class_Terminate()
 90    
 91    '定数
 92    Const FUNC_NAME As String = "Class_Terminate"
 93    
 94    '変数
 95    
 96    On Error GoTo ErrorHandler
 97    
 98    'ブックの保存 To:=デスクトップ
 99    With CreateObject("WScript.Shell")
100        myWorkBook.SaveAs .SpecialFolders("Desktop") & "\" & "Test-Excel-Class-" & Format(Now, "yyyymmddhhnnss") & ".xlsx"
101    End With
102    
103    '設定のリストア
104    With myXlApp
105        .ScreenUpdating = True
106        .DisplayAlerts = True
107    End With
108    
109    'クローズ
110    myWorkBook.Close
111    myXlApp.Quit
112
113ExitHandler:
114    
115    Set dicWorkSheet = Nothing
116    Set myWorkBook = Nothing
117    Set myXlApp = Nothing
118    
119    Exit Sub
120    
121ErrorHandler:
122
123    MsgBox "エラーが発生したため、マクロを終了します。" & _
124           vbLf & _
125           "関数名:" & FUNC_NAME & _
126           vbLf & _
127           "エラー番号:" & Err.Number & vbNewLine & _
128           Err.Description, vbCritical, TOOL_NAME
129        
130    GoTo ExitHandler
131        
132End Sub
133
134
135
136'******************************************************************************************
137'*関数名    :addNewSheet
138'*機能      :ブックにシートを追加
139'*引数      :
140'*戻り値    :追加されたシートオブジェクト
141'******************************************************************************************
142Public Function addNewSheet() As Object
143    
144    '定数
145    Const FUNC_NAME As String = "addNewSheet"
146    
147    '変数
148    Dim ws As Object
149    
150    On Error GoTo ErrorHandler
151
152    Set addNewSheet = Nothing
153    
154    currentSheetNum = currentSheetNum + 1
155    '最後尾にシート追加
156    Set ws = myWorkBook.WorkSheets.Add(After:=myWorkBook.WorkSheets(myWorkBook.WorkSheets.Count))
157    dicWorkSheet.Add currentSheetNum, ws
158    
159    Set addNewSheet = ws
160    
161ExitHandler:
162
163    Exit Function
164    
165ErrorHandler:
166
167    MsgBox "エラーが発生したため、マクロを終了します。" & _
168           vbLf & _
169           "関数名:" & FUNC_NAME & _
170           vbLf & _
171           "エラー番号:" & Err.Number & vbNewLine & _
172           Err.Description, vbCritical, TOOL_NAME
173        
174    GoTo ExitHandler
175        
176End Function
177
178
179

カプセル化

上記のコードを見るとわかるように、
クラス内部の変数myXlApp、myWorkBookなどはPrivateのスコープで宣言されている。
そのため、
各種Property Getを通してのみ取得可能であり、
直接値を参照して取得したり設定したりすることはできない。

唯一、dicWorkSheet(ワークシートが格納されているDictionaryオブジェクト)は、
関数addNewSheetを通してのみDicitonaryのKey&Valueペアの設定が可能である。
その動作すらも、「最後尾にシートを追加する」という定型的な動作以外は許されていない。

このように、クラス内部の変数が特定の方法以外で値の設定ができないように守られていることを カプセル化 と呼ぶ。

カプセル化により、クラスの変数が不正に設定されたり削除されたりすることを防ぎ、バグの発生を防いだり、コードの見通しを良くしたりする。

初期化処理・終了処理

こちらも上記のコードを見るとわかるように、
Class_Initializeにおいて、クラスのオブジェクトを生成した際に
最低限やりたい処理をまとめて行うことができるようになっている。

最低限やりたい処理とは、
新規Excelアプリケーションのインタフェースの生成、
新規Excelブックの生成、
ブックの最初のワークシートをDictionaryに格納すること、
Excelアプリケーションの動作をバックグラウンドにし、画面更新を停止し、警告アラートを抑制することである。

Class_Terminateは逆に、
Excelアプリケーションの設定のリストアを行い、
ブックをクローズし、Excelアプリケーションを終了させる。
また、PCのデスクトップにブックを保存させる。

このように、最低限やりたい処理をかならず実行するようにできているため、
わざわざ外部のコードで記述する必要もないし、
見落としてコーディングし忘れたりする恐れもなくなる。

特に、Excelアプリケーションのオブジェクトの場合、
オブジェクトを格納した変数がグローバル変数であり、かつ処理後にNothingが代入されなかった場合、
ずっとExcelがバックグラウンドで起動し続けることになる ため、注意が必要。

その他のコード

getTableHeader

引数で指定したテーブルのヘッダー文字列配列を、
値返却用の変数に格納して呼び出し元に返す。

 1'******************************************************************************************
 2'*関数名    :getTableHeader
 3'*機能      :テーブルのヘッダー文字列配列を取得
 4'*引数      :テーブル名
 5'*引数      :結果返却用
 6'*戻り値    :True > 正常終了、False > 異常終了
 7'******************************************************************************************
 8Public Function getTableHeader(ByVal tblName As String, ByRef pArrHeader() As String) As Boolean
 9    
10    '定数
11    Const FUNC_NAME As String = "getTableHeader"
12    
13    '変数
14    Dim i As Long
15    
16    On Error GoTo ErrorHandler
17
18    getTableHeader = False
19    Erase pArrHeader
20    
21    With db.TableDefs(tblName)
22        ReDim pArrHeader(0 To .Fields.Count - 1)
23        For i = 0 To .Fields.Count - 1
24            pArrHeader(i) = .Fields(i).Name
25        Next
26    End With
27    
28    getTableHeader = True
29    
30ExitHandler:
31
32    Exit Function
33    
34ErrorHandler:
35
36    MsgBox "エラーが発生したため、マクロを終了します。" & _
37           vbLf & _
38           "関数名:" & FUNC_NAME & _
39           vbLf & _
40           "エラー番号:" & Err.Number & vbNewLine & _
41           Err.Description, vbCritical, TOOL_NAME
42        
43    GoTo ExitHandler
44        
45End Function

getTableDataBySQL

引数のSQL文字列をもとに得られたレコードセットのデータを二次元配列として取得し、
値返却用の変数に格納して呼び出し元に返す。

 1'******************************************************************************************
 2'*関数名    :getTableDataBySQL
 3'*機能      :SQL文字列より得られたレコードセットのデータを二次元配列として取得
 4'*引数      :対象SQL文字列
 5'*引数      :結果返却用
 6'*戻り値    :True > 正常終了、False > 異常終了
 7'******************************************************************************************
 8Public Function getTableDataBySQL(ByVal sql As String, ByRef arrData() As Variant) As Boolean
 9    
10    '定数
11    Const FUNC_NAME As String = "getTableDataBySQL"
12    
13    '変数
14    Dim rs As DAO.Recordset
15    Dim i As Long
16    Dim j As Long
17    
18    On Error GoTo ErrorHandler
19
20    getTableDataBySQL = False
21    Erase arrData
22    
23    Set rs = db.OpenRecordset(sql)
24    With rs
25        If .EOF Then GoTo TruePoint
26        .MoveLast
27        ReDim arrData(0 To .RecordCount - 1, 0 To .Fields.Count - 1)
28        .MoveFirst
29        
30        i = 0
31        Do Until .EOF
32            For j = 0 To .Fields.Count - 1
33                arrData(i, j) = .Fields(j).Value
34            Next j
35            i = i + 1
36            .MoveNext
37        Loop
38    End With
39
40TruePoint:
41
42    getTableDataBySQL = True
43    
44ExitHandler:
45    
46    If Not rs Is Nothing Then rs.Clone: Set rs = Nothing
47    
48    Exit Function
49    
50ErrorHandler:
51
52    MsgBox "エラーが発生したため、マクロを終了します。" & _
53           vbLf & _
54           "関数名:" & FUNC_NAME & _
55           vbLf & _
56           "エラー番号:" & Err.Number & vbNewLine & _
57           Err.Description, vbCritical, TOOL_NAME
58        
59    GoTo ExitHandler
60        
61End Function

postDataToSheet

引数のシートオブジェクトの
一行目に指定されたテーブルヘッダー文字列配列を、
二行目以降にテーブルデータの二次元配列データを転記する。

 1
 2'******************************************************************************************
 3'*関数名    :postDataToSheet
 4'*機能      :シートに配列データを転記する
 5'*引数      :対象シート
 6'*引数      :シート名
 7'*引数      :ヘッダー配列
 8'*引数      :データ配列
 9'*戻り値    :True > 正常終了、False > 異常終了
10'******************************************************************************************
11Public Function postDataToSheet( _
12    ByVal tgtSheet As Object, _
13    ByVal sheetName As String, _
14    ByVal pArrHeader As Variant, _
15    ByVal pArrData As Variant _
16) As Boolean
17    
18    '定数
19    Const FUNC_NAME As String = "postDataToSheet"
20    
21    '変数
22    
23    On Error GoTo ErrorHandler
24
25    postDataToSheet = False
26    
27    With tgtSheet
28        .Name = sheetName
29        .Range(.cells(1, 1), .cells(1, UBound(pArrHeader) - LBound(pArrHeader) + 1)).Value = pArrHeader
30        .Range(.cells(2, 1), .cells(UBound(pArrData, 1) - LBound(pArrData, 1) + 2, UBound(pArrData, 2) - LBound(pArrData, 2) + 1)).Value = pArrData
31        '罫線
32        .Range(.cells(1, 1), .cells(UBound(pArrData, 1) - LBound(pArrData, 1) + 2, UBound(pArrData, 2) - LBound(pArrData, 2) + 1)).Borders.LineStyle = xlContinuous
33        '列幅調整
34        .Range(.Columns(1), .Columns(UBound(pArrHeader) - LBound(pArrHeader) + 1)).AutoFit
35    End With
36
37    postDataToSheet = True
38    
39ExitHandler:
40
41    Exit Function
42    
43ErrorHandler:
44
45    MsgBox "エラーが発生したため、マクロを終了します。" & _
46           vbLf & _
47           "関数名:" & FUNC_NAME & _
48           vbLf & _
49           "エラー番号:" & Err.Number & vbNewLine & _
50           Err.Description, vbCritical, TOOL_NAME
51        
52    GoTo ExitHandler
53        
54End Function
55

getJsonFromAPI

指定URLのWebAPIからJson文字列を取得する。
本ツールではGoogle Apps Script(GAS)上のAPIを叩いて
商品テーブルデータのもととなるJsonを取得する。

 1
 2'******************************************************************************************
 3'*関数名    :getJsonFromAPI
 4'*機能      :指定URLのAPIからJson文字列を取得
 5'*引数      :URL
 6'*戻り値    :Json文字列(Parse前)
 7'******************************************************************************************
 8Public Function getJsonFromAPI(URL As String) As String
 9
10    '定数
11    Const FUNC_NAME As String = "getJsonFromAPI"
12    
13    '変数
14    Dim objXMLHttp As Object
15    
16    On Error GoTo ErrorHandler
17
18    getJsonFromAPI = ""
19    
20    Set objXMLHttp = CreateObject("MSXML2.ServerXMLHTTP.6.0")
21        objXMLHttp.Open "GET", URL, False
22        objXMLHttp.Send
23
24
25    getJsonFromAPI = objXMLHttp.responseText
26    
27ExitHandler:
28
29    Exit Function
30    
31ErrorHandler:
32
33    MsgBox "エラーが発生したため、マクロを終了します。" & _
34           vbLf & _
35           "関数名:" & FUNC_NAME & _
36           vbLf & _
37           "エラー番号:" & Err.Number & vbNewLine & _
38           Err.Description, vbCritical, TOOL_NAME
39        
40    GoTo ExitHandler
41        
42End Function
43

実行すると

例えばフォームで一番下の「実行_(1)+(2)+(3)+(4)」ボタンを実行すると、
次のようなExcelブックがデスクトップ上に生成される。

単価1万超えデータ

商品コードがBから始まるデータ

WebAPIから取得したデータ

サンプルとソースコードについて

こちらをご参照ください。

終わりに

GASを用いると簡単にJSONを返すAPIが作れるみたいですね。
知らなかったです。

JSONを返す無料APIを3分で作る方法 - Qiita

関連記事

comments powered by Disqus

Translations: