VBAコードが長くなって 「どこに何が書いてあるか分からない…」 と困っていませんか?
クラスモジュール を使えば、コードを劇的に整理して 可読性・保守性・再利用性 を大幅に向上できます!
この記事では以下の悩みを実例コード15パターンで解決します:
- クラスモジュールって何?どう使うの?
- 標準モジュールとの違いは?メリットは?
- プロパティ・メソッドの作り方が分からない
- Property Get/Set/Let の使い分けを知りたい
- Initialize/Terminate イベントの活用法を習得したい
- 複数インスタンスをコレクション化したい
クラスモジュールとは?
クラスモジュール = 独自のオブジェクト(型)を作る設計図
Excelには Range、Worksheet、Workbook などの組み込みオブジェクトがありますが、クラスモジュールを使うと 自分専用のオブジェクト を作成できます。
身近な例: 「社員」オブジェクト
社員オブジェクト
├─ プロパティ(属性)
│ ├─ 名前
│ ├─ 年齢
│ └─ 部署
└─ メソッド(動作)
├─ 自己紹介する
└─ 給与を計算する
このような データ(プロパティ)と処理(メソッド)をひとまとめ にしたものがクラスです。
オブジェクト指向の基本用語
| 用語 | 意味 | 例 |
|---|---|---|
| クラス | オブジェクトの設計図 | 「社員」という設計図 |
| インスタンス | クラスから作られた実体 | 「田中太郎さん」という実際の社員 |
| プロパティ | オブジェクトの属性(データ) | 名前、年齢、部署 |
| メソッド | オブジェクトの動作(処理) | 自己紹介する、給与を計算する |
標準モジュール vs クラスモジュール
比較表
| 項目 | 標準モジュール | クラスモジュール |
|---|---|---|
| 用途 | 汎用的な処理・マクロ | データと処理をひとまとめ |
| インスタンス化 | 不要 | 必要(Set obj = New クラス名) |
| 複数作成 | できない | 何個でも作成可能 |
| データ管理 | グローバル変数・引数で管理 | プロパティで管理 |
| 可読性 | 処理が分散しやすい | データと処理がまとまる |
| 再利用性 | 低い | 高い |
| 保守性 | 低い(大規模だと複雑) | 高い(責任範囲が明確) |
使い分けの基本ルール
標準モジュールを使う場合:
- 単純なマクロ(ボタンクリック処理など)
- 汎用的なユーティリティ関数(日付フォーマット等)
- 小規模プロジェクト(100行以内)
クラスモジュールを使う場合:
- 複数の関連データをまとめて扱う
- 同じ種類のデータを複数作成する(社員、商品、注文等)
- 大規模プロジェクト(500行以上)
- チーム開発・長期保守
クラスモジュールの3大メリット
コードの整理・可読性向上
❌ 標準モジュールのみ(散らかっている)
' グローバル変数が大量
Dim employeeName As String
Dim employeeAge As Integer
Dim employeeDept As String
Dim productName As String
Dim productPrice As Long
' ... (変数が100個...)
Sub ProcessEmployee()
' 社員処理
End Sub
Sub ProcessProduct()
' 商品処理
End Sub
' ... (関数が50個...)
クラスモジュール(整理されている)
' 社員クラス
Class Employee
Name, Age, Dept
CalculateSalary()
End Class
' 商品クラス
Class Product
Name, Price, Stock
CheckStock()
End Class
データと処理が クラスごとに綺麗に分離 されます。
再利用性向上
一度作ったクラスは 他のプロジェクトでもコピペするだけ で使えます。
' プロジェクトA: 社員管理システム
Dim emp As New Employee
' プロジェクトB: 給与計算システム
Dim emp As New Employee ' 同じクラスを再利用!
保守性向上
変更が必要な箇所が クラス内に限定 されるため、バグ修正・機能追加が容易です。
' 社員クラスの給与計算ロジック変更
' → Employeeクラス内のCalculateSalaryメソッドだけ修正すればOK
' → 他のコードに影響なし!
最も簡単なクラスを作る
クラスモジュールの追加方法
- VBE(Visual Basic Editor)を開く(
Alt + F11) - 挿入 → クラスモジュール
- プロパティウィンドウ(F4)で オブジェクト名 を変更(例:
Person)
最も簡単なPersonクラス
クラスモジュール: Person
' プロパティ(Public変数)
Public Name As String
Public Age As Integer
' メソッド
Public Sub Greet()
MsgBox "こんにちは、" & Name & "です。" & Age & "歳です。"
End Sub
標準モジュールから使う
標準モジュール
Sub TestPerson()
' インスタンス化(実体を作る)
Dim person1 As New Person
' プロパティに値を設定
person1.Name = "田中太郎"
person1.Age = 30
' メソッドを呼び出す
person1.Greet ' → "こんにちは、田中太郎です。30歳です。"
' 複数インスタンス作成可能
Dim person2 As New Person
person2.Name = "佐藤花子"
person2.Age = 25
person2.Greet ' → "こんにちは、佐藤花子です。25歳です。"
' クリーンアップ
Set person1 = Nothing
Set person2 = Nothing
End Sub
ポイント:
Dim person1 As New Personでインスタンス化person1.Nameのように ドット(.) でプロパティ・メソッドにアクセス- 同じクラスから 複数のインスタンス を作成可能
プロパティの作り方(Property Get/Let/Set)
Public変数をそのまま公開すると データの整合性が保てない ため、Property プロシージャを使います。
Property Get/Let/Set の違い
| プロシージャ | 用途 | 例 |
|---|---|---|
| Property Get | 値を取得 | 価格 = product.Price |
| Property Let | 値を設定(数値・文字列) | product.Price = 1000 |
| Property Set | オブジェクトを設定 | Set person.Manager = mgr |
基本構文
クラスモジュール: Product
' プライベート変数(外部からアクセス不可)
Private mPrice As Long
' Property Get(値を取得)
Public Property Get Price() As Long
Price = mPrice
End Property
' Property Let(値を設定)
Public Property Let Price(ByVal value As Long)
' 入力チェック
If value < 0 Then
Err.Raise vbObjectError + 1, , "価格は0以上を指定してください"
End If
mPrice = value
End Property
標準モジュール
Sub TestProperty()
Dim product As New Product
' Property Let が呼ばれる
product.Price = 1000
' Property Get が呼ばれる
Debug.Print product.Price ' → 1000
' エラーチェックが働く
product.Price = -100 ' → エラー!
Set product = Nothing
End Sub
Property Set(オブジェクト設定)
クラスモジュール: Employee
Private mManager As Employee
Public Property Get Manager() As Employee
Set Manager = mManager
End Property
Public Property Set Manager(ByVal value As Employee)
Set mManager = value
End Property
標準モジュール
Sub TestPropertySet()
Dim emp As New Employee
Dim mgr As New Employee
emp.Name = "田中太郎"
mgr.Name = "山田課長"
' Property Set が呼ばれる
Set emp.Manager = mgr
' Property Get が呼ばれる
Debug.Print emp.Manager.Name ' → "山田課長"
Set emp = Nothing
Set mgr = Nothing
End Sub
命名規則
| 要素 | 命名例 | 説明 |
|---|---|---|
| Private変数 | mPrice, m_Price | m は member の略 |
| Property | Price | 大文字始まり |
| 引数 | value, newValue | 小文字始まり |
メソッドの作り方
メソッド = クラスの 動作・処理 を定義する関数・サブルーチン
Sub メソッド(戻り値なし)
クラスモジュール: BankAccount
Private mBalance As Long
Public Property Get Balance() As Long
Balance = mBalance
End Property
' メソッド: 入金
Public Sub Deposit(ByVal amount As Long)
If amount <= 0 Then
Err.Raise vbObjectError + 1, , "入金額は1以上を指定してください"
End If
mBalance = mBalance + amount
End Sub
' メソッド: 出金
Public Sub Withdraw(ByVal amount As Long)
If amount <= 0 Then
Err.Raise vbObjectError + 2, , "出金額は1以上を指定してください"
End If
If mBalance < amount Then
Err.Raise vbObjectError + 3, , "残高不足です"
End If
mBalance = mBalance - amount
End Sub
標準モジュール
Sub TestBankAccount()
Dim account As New BankAccount
account.Deposit 10000 ' 10,000円入金
account.Withdraw 3000 ' 3,000円出金
Debug.Print account.Balance ' → 7,000
Set account = Nothing
End Sub
Function メソッド(戻り値あり)
クラスモジュール: Rectangle
Private mWidth As Double
Private mHeight As Double
Public Property Let Width(ByVal value As Double)
mWidth = value
End Property
Public Property Let Height(ByVal value As Double)
mHeight = value
End Property
' メソッド: 面積を計算
Public Function GetArea() As Double
GetArea = mWidth * mHeight
End Function
' メソッド: 周囲の長さを計算
Public Function GetPerimeter() As Double
GetPerimeter = (mWidth + mHeight) * 2
End Function
標準モジュール
Sub TestRectangle()
Dim rect As New Rectangle
rect.Width = 10
rect.Height = 5
Debug.Print "面積: " & rect.GetArea() ' → 50
Debug.Print "周囲: " & rect.GetPerimeter() ' → 30
Set rect = Nothing
End Sub
Initialize/Terminateイベント
Class_Initialize イベント
インスタンス作成時に 自動実行 される初期化処理
クラスモジュール: Logger
Private mLogFile As String
' Initialize イベント
Private Sub Class_Initialize()
' インスタンス作成時に自動実行
mLogFile = "C:\Logs\app_" & Format(Now, "yyyymmdd") & ".log"
Debug.Print "Logger初期化: " & mLogFile
End Sub
Public Sub WriteLog(ByVal message As String)
' FSO でファイル書き込み
Dim fso As Object, txt As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set txt = fso.OpenTextFile(mLogFile, 8, True) ' ForAppending=8
txt.WriteLine Format(Now, "yyyy-mm-dd hh:nn:ss") & " " & message
txt.Close
Set txt = Nothing
Set fso = Nothing
End Sub
標準モジュール
Sub TestLogger()
Dim logger As New Logger
' ↑ New した瞬間に Class_Initialize が自動実行される!
logger.WriteLog "処理開始"
logger.WriteLog "処理完了"
Set logger = Nothing
End Sub
Class_Terminate イベント
インスタンス破棄時に 自動実行 される終了処理
クラスモジュール: DatabaseConnection
Private mConnection As Object
Private Sub Class_Initialize()
' DB接続
Set mConnection = CreateObject("ADODB.Connection")
mConnection.Open "接続文字列..."
Debug.Print "DB接続成功"
End Sub
Private Sub Class_Terminate()
' DB切断
If Not mConnection Is Nothing Then
If mConnection.State = 1 Then
mConnection.Close
Debug.Print "DB切断完了"
End If
Set mConnection = Nothing
End If
End Sub
Public Sub ExecuteQuery(ByVal sql As String)
mConnection.Execute sql
End Sub
標準モジュール
Sub TestDBConnection()
Dim db As New DatabaseConnection
' ↑ Initialize で自動的にDB接続
db.ExecuteQuery "INSERT INTO ..."
Set db = Nothing
' ↑ Terminate で自動的にDB切断
End Sub
実例: 社員クラスを作る
クラスモジュール: Employee
' プライベート変数
Private mEmployeeID As Long
Private mName As String
Private mAge As Integer
Private mDepartment As String
Private mBaseSalary As Long
' プロパティ: 社員ID(読み取り専用)
Public Property Get EmployeeID() As Long
EmployeeID = mEmployeeID
End Property
' プロパティ: 名前
Public Property Get Name() As String
Name = mName
End Property
Public Property Let Name(ByVal value As String)
If Len(value) = 0 Then
Err.Raise vbObjectError + 1, , "名前は必須です"
End If
mName = value
End Property
' プロパティ: 年齢
Public Property Get Age() As Integer
Age = mAge
End Property
Public Property Let Age(ByVal value As Integer)
If value < 18 Or value > 70 Then
Err.Raise vbObjectError + 2, , "年齢は18~70の範囲で指定してください"
End If
mAge = value
End Property
' プロパティ: 部署
Public Property Get Department() As String
Department = mDepartment
End Property
Public Property Let Department(ByVal value As String)
mDepartment = value
End Property
' プロパティ: 基本給
Public Property Get BaseSalary() As Long
BaseSalary = mBaseSalary
End Property
Public Property Let BaseSalary(ByVal value As Long)
If value < 0 Then
Err.Raise vbObjectError + 3, , "基本給は0以上を指定してください"
End If
mBaseSalary = value
End Property
' Initialize イベント
Private Sub Class_Initialize()
' IDを自動採番(簡易版)
mEmployeeID = CLng(Format(Now, "yyyymmddhhnnss"))
End Sub
' メソッド: 給与計算
Public Function CalculateSalary() As Long
' 基本給 + 年齢手当(年齢×1000円)
CalculateSalary = mBaseSalary + (mAge * 1000)
End Function
' メソッド: 自己紹介
Public Sub Introduce()
Dim msg As String
msg = "社員ID: " & mEmployeeID & vbCrLf & _
"名前: " & mName & vbCrLf & _
"年齢: " & mAge & "歳" & vbCrLf & _
"部署: " & mDepartment & vbCrLf & _
"給与: " & Format(CalculateSalary, "#,##0") & "円"
MsgBox msg
End Sub
標準モジュール
Sub TestEmployee()
Dim emp As New Employee
' プロパティ設定
emp.Name = "田中太郎"
emp.Age = 30
emp.Department = "営業部"
emp.BaseSalary = 250000
' メソッド呼び出し
Debug.Print "給与: " & emp.CalculateSalary ' → 280,000円
emp.Introduce
Set emp = Nothing
End Sub
実例: 商品クラスを作る
クラスモジュール: Product
Private mProductID As String
Private mProductName As String
Private mPrice As Long
Private mStock As Long
' プロパティ
Public Property Get ProductID() As String
ProductID = mProductID
End Property
Public Property Let ProductID(ByVal value As String)
mProductID = value
End Property
Public Property Get ProductName() As String
ProductName = mProductName
End Property
Public Property Let ProductName(ByVal value As String)
mProductName = value
End Property
Public Property Get Price() As Long
Price = mPrice
End Property
Public Property Let Price(ByVal value As Long)
If value < 0 Then
Err.Raise vbObjectError + 1, , "価格は0以上を指定してください"
End If
mPrice = value
End Property
Public Property Get Stock() As Long
Stock = mStock
End Property
Public Property Let Stock(ByVal value As Long)
If value < 0 Then
Err.Raise vbObjectError + 2, , "在庫は0以上を指定してください"
End If
mStock = value
End Property
' メソッド: 在庫確認
Public Function IsInStock(ByVal quantity As Long) As Boolean
IsInStock = (mStock >= quantity)
End Function
' メソッド: 在庫減算
Public Sub ReduceStock(ByVal quantity As Long)
If Not IsInStock(quantity) Then
Err.Raise vbObjectError + 3, , "在庫不足です"
End If
mStock = mStock - quantity
End Sub
' メソッド: 在庫追加
Public Sub AddStock(ByVal quantity As Long)
If quantity <= 0 Then
Err.Raise vbObjectError + 4, , "追加数量は1以上を指定してください"
End If
mStock = mStock + quantity
End Sub
' メソッド: 合計金額計算
Public Function GetTotalPrice(ByVal quantity As Long) As Long
GetTotalPrice = mPrice * quantity
End Function
標準モジュール
Sub TestProduct()
Dim product As New Product
product.ProductID = "P001"
product.ProductName = "ノートパソコン"
product.Price = 120000
product.Stock = 10
' 在庫確認
If product.IsInStock(3) Then
Debug.Print "3台購入可能です"
Debug.Print "合計: " & Format(product.GetTotalPrice(3), "#,##0") & "円"
' 在庫減算
product.ReduceStock 3
Debug.Print "残り在庫: " & product.Stock ' → 7
End If
' 在庫追加
product.AddStock 5
Debug.Print "補充後在庫: " & product.Stock ' → 12
Set product = Nothing
End Sub
複数インスタンスとCollection
Collectionで複数のインスタンスを管理
標準モジュール
Sub TestEmployeeCollection()
' Collectionオブジェクト作成
Dim employees As New Collection
' 社員1
Dim emp1 As New Employee
emp1.Name = "田中太郎"
emp1.Age = 30
emp1.Department = "営業部"
emp1.BaseSalary = 250000
employees.Add emp1
' 社員2
Dim emp2 As New Employee
emp2.Name = "佐藤花子"
emp2.Age = 28
emp2.Department = "開発部"
emp2.BaseSalary = 280000
employees.Add emp2
' 社員3
Dim emp3 As New Employee
emp3.Name = "鈴木一郎"
emp3.Age = 35
emp3.Department = "営業部"
emp3.BaseSalary = 300000
employees.Add emp3
' 全社員の給与合計を計算
Dim totalSalary As Long
Dim emp As Employee
For Each emp In employees
totalSalary = totalSalary + emp.CalculateSalary
Next emp
Debug.Print "給与合計: " & Format(totalSalary, "#,##0") & "円"
' → 給与合計: 923,000円
' クリーンアップ
Set employees = Nothing
End Sub
Dictionaryで管理(キー指定で高速検索)
Sub TestEmployeeDictionary()
Dim employees As Object
Set employees = CreateObject("Scripting.Dictionary")
' 社員を追加(キー: 社員ID)
Dim emp1 As New Employee
emp1.Name = "田中太郎"
emp1.Age = 30
emp1.BaseSalary = 250000
employees.Add emp1.EmployeeID, emp1
Dim emp2 As New Employee
emp2.Name = "佐藤花子"
emp2.Age = 28
emp2.BaseSalary = 280000
employees.Add emp2.EmployeeID, emp2
' IDで検索(高速)
Dim targetID As Long
targetID = emp1.EmployeeID
If employees.Exists(targetID) Then
Dim emp As Employee
Set emp = employees(targetID)
Debug.Print emp.Name ' → "田中太郎"
End If
Set employees = Nothing
End Sub
読み取り専用プロパティ
Property Get のみ 実装して Property Let/Set を実装しない = 読み取り専用
クラスモジュール: Order
Private mOrderID As String
Private mOrderDate As Date
Private mTotalAmount As Long
' 読み取り専用プロパティ
Public Property Get OrderID() As String
OrderID = mOrderID
End Property
Public Property Get OrderDate() As Date
OrderDate = mOrderDate
End Property
Public Property Get TotalAmount() As Long
TotalAmount = mTotalAmount
End Property
' Initialize で自動設定
Private Sub Class_Initialize()
mOrderID = "ORD" & Format(Now, "yyyymmddhhnnss")
mOrderDate = Date
mTotalAmount = 0
End Sub
Public Sub AddItem(ByVal price As Long, ByVal quantity As Long)
mTotalAmount = mTotalAmount + (price * quantity)
End Sub
標準モジュール
Sub TestOrder()
Dim order As New Order
' 読み取りOK
Debug.Print order.OrderID
Debug.Print order.OrderDate
' 書き込み不可(エラー)
order.OrderID = "XXX" ' → エラー!
Set order = Nothing
End Sub
デフォルトプロパティ
デフォルトプロパティ = プロパティ名を省略してアクセス可能
クラスモジュール: Range風クラス
Private mValue As Variant
' Property Get の属性でデフォルト指定
Public Property Get Value() As Variant
Attribute Value.VB_UserMemId = 0 ' デフォルトプロパティ指定
Value = mValue
End Property
Public Property Let Value(ByVal newValue As Variant)
mValue = newValue
End Property
注意: Attribute はコードエディタでは設定できず、エクスポート→編集→インポートが必要なため 実務では使用頻度低い。
実務テンプレート5選
データベースレコードをクラス化
クラスモジュール: CustomerRecord
Private mCustomerID As Long
Private mCustomerName As String
Private mEmail As String
Private mPhone As String
Public Property Get CustomerID() As Long: CustomerID = mCustomerID: End Property
Public Property Let CustomerID(v As Long): mCustomerID = v: End Property
Public Property Get CustomerName() As String: CustomerName = mCustomerName: End Property
Public Property Let CustomerName(v As String): mCustomerName = v: End Property
Public Property Get Email() As String: Email = mEmail: End Property
Public Property Let Email(v As String): mEmail = v: End Property
Public Property Get Phone() As String: Phone = mPhone: End Property
Public Property Let Phone(v As String): mPhone = v: End Property
' Recordsetから読み込み
Public Sub LoadFromRecordset(ByVal rs As Object)
mCustomerID = rs.Fields("CustomerID").Value
mCustomerName = rs.Fields("CustomerName").Value
mEmail = rs.Fields("Email").Value
mPhone = rs.Fields("Phone").Value
End Sub
' DBへ保存
Public Sub SaveToDatabase(ByVal cn As Object)
Dim sql As String
sql = "UPDATE Customers SET " & _
"CustomerName = '" & mCustomerName & "', " & _
"Email = '" & mEmail & "', " & _
"Phone = '" & mPhone & "' " & _
"WHERE CustomerID = " & mCustomerID
cn.Execute sql
End Sub
設定ファイル管理クラス
クラスモジュール: AppConfig
Private mConfigFile As String
Private mSettings As Object ' Dictionary
Private Sub Class_Initialize()
mConfigFile = ThisWorkbook.Path & "\config.ini"
Set mSettings = CreateObject("Scripting.Dictionary")
LoadConfig
End Sub
Private Sub LoadConfig()
Dim fso As Object, txt As Object
Set fso = CreateObject("Scripting.FileSystemObject")
If Not fso.FileExists(mConfigFile) Then Exit Sub
Set txt = fso.OpenTextFile(mConfigFile, 1) ' ForReading=1
Do Until txt.AtEndOfStream
Dim line As String
line = txt.ReadLine
If InStr(line, "=") > 0 Then
Dim parts As Variant
parts = Split(line, "=")
mSettings.Add Trim(parts(0)), Trim(parts(1))
End If
Loop
txt.Close
Set txt = Nothing
Set fso = Nothing
End Sub
Public Function GetSetting(ByVal key As String, Optional ByVal defaultValue As String = "") As String
If mSettings.Exists(key) Then
GetSetting = mSettings(key)
Else
GetSetting = defaultValue
End If
End Function
Public Sub SetSetting(ByVal key As String, ByVal value As String)
If mSettings.Exists(key) Then
mSettings(key) = value
Else
mSettings.Add key, value
End If
End Sub
Public Sub SaveConfig()
Dim fso As Object, txt As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set txt = fso.CreateTextFile(mConfigFile, True)
Dim key As Variant
For Each key In mSettings.Keys
txt.WriteLine key & "=" & mSettings(key)
Next key
txt.Close
Set txt = Nothing
Set fso = Nothing
End Sub
バリデーションクラス
クラスモジュール: Validator
' メール形式チェック
Public Function IsValidEmail(ByVal email As String) As Boolean
Dim regex As Object
Set regex = CreateObject("VBScript.RegExp")
regex.Pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
IsValidEmail = regex.Test(email)
Set regex = Nothing
End Function
' 電話番号形式チェック
Public Function IsValidPhone(ByVal phone As String) As Boolean
Dim regex As Object
Set regex = CreateObject("VBScript.RegExp")
regex.Pattern = "^0\d{1,4}-\d{1,4}-\d{4}$"
IsValidPhone = regex.Test(phone)
Set regex = Nothing
End Function
' 郵便番号形式チェック
Public Function IsValidPostalCode(ByVal postalCode As String) As Boolean
Dim regex As Object
Set regex = CreateObject("VBScript.RegExp")
regex.Pattern = "^\d{3}-\d{4}$"
IsValidPostalCode = regex.Test(postalCode)
Set regex = Nothing
End Function
' 日付範囲チェック
Public Function IsDateInRange(ByVal targetDate As Date, ByVal startDate As Date, ByVal endDate As Date) As Boolean
IsDateInRange = (targetDate >= startDate And targetDate <= endDate)
End Function
ログ出力クラス
クラスモジュール: Logger
Private mLogFile As String
Private mLogLevel As Integer ' 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR
Public Enum LogLevel
DEBUG = 1
INFO = 2
WARN = 3
ERRR = 4
End Enum
Private Sub Class_Initialize()
mLogFile = ThisWorkbook.Path & "\app.log"
mLogLevel = LogLevel.INFO
End Sub
Public Property Let Level(ByVal value As LogLevel)
mLogLevel = value
End Property
Private Sub WriteLog(ByVal level As String, ByVal message As String)
Dim fso As Object, txt As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set txt = fso.OpenTextFile(mLogFile, 8, True) ' ForAppending=8
Dim logMessage As String
logMessage = Format(Now, "yyyy-mm-dd hh:nn:ss") & " [" & level & "] " & message
txt.WriteLine logMessage
txt.Close
Set txt = Nothing
Set fso = Nothing
End Sub
Public Sub Debug(ByVal message As String)
If mLogLevel <= LogLevel.DEBUG Then WriteLog "DEBUG", message
End Sub
Public Sub Info(ByVal message As String)
If mLogLevel <= LogLevel.INFO Then WriteLog "INFO", message
End Sub
Public Sub Warn(ByVal message As String)
If mLogLevel <= LogLevel.WARN Then WriteLog "WARN", message
End Sub
Public Sub Error(ByVal message As String)
If mLogLevel <= LogLevel.ERRR Then WriteLog "ERROR", message
End Sub
使用例
Sub TestLogger()
Dim logger As New Logger
logger.Level = LogLevel.INFO
logger.Debug "デバッグメッセージ" ' 出力されない
logger.Info "処理開始" ' 出力される
logger.Warn "警告メッセージ" ' 出力される
logger.Error "エラー発生" ' 出力される
Set logger = Nothing
End Sub
Excelシート操作クラス
クラスモジュール: SheetHelper
Private mSheet As Worksheet
Public Property Set Sheet(ByVal ws As Worksheet)
Set mSheet = ws
End Property
Public Property Get Sheet() As Worksheet
Set Sheet = mSheet
End Property
' 最終行取得
Public Function GetLastRow(ByVal col As Long) As Long
GetLastRow = mSheet.Cells(mSheet.Rows.Count, col).End(xlUp).Row
End Function
' 最終列取得
Public Function GetLastColumn(ByVal row As Long) As Long
GetLastColumn = mSheet.Cells(row, mSheet.Columns.Count).End(xlToLeft).Column
End Function
' データ範囲を配列で取得
Public Function GetDataArray(ByVal startRow As Long, ByVal startCol As Long) As Variant
Dim lastRow As Long, lastCol As Long
lastRow = GetLastRow(startCol)
lastCol = GetLastColumn(startRow)
GetDataArray = mSheet.Range(mSheet.Cells(startRow, startCol), _
mSheet.Cells(lastRow, lastCol)).Value
End Function
' データクリア
Public Sub ClearData(ByVal startRow As Long)
Dim lastRow As Long
lastRow = GetLastRow(1)
If lastRow >= startRow Then
mSheet.Rows(startRow & ":" & lastRow).Delete
End If
End Sub
よくある質問(FAQ 7問)
Q1. クラスモジュールを使うと処理が遅くなりますか?
A. ほとんど影響ありません。
クラスモジュールのオーバーヘッドは微々たるもので、実務上は体感できません。むしろコードが整理されて 保守性・可読性が向上 するメリットの方が大きいです。
Q2. Private変数と Public Property、どちらを使うべきですか?
A. Public Property 推奨。
| 項目 | Private変数 + Property | Public変数 |
|---|---|---|
| データ検証 | ✔ できる | ✗ できない |
| 読み取り専用 | ✔ 可能 | ✗ 不可 |
| 将来の変更 | ✔ 内部実装変更可能 | ✗ 全コード修正必要 |
| カプセル化 | ✔ 保護される | ✗ 直接アクセス |
例外: 簡易なクラス・プロトタイプ開発では Public変数でもOK。
Q3. Property Let と Property Set の使い分けは?
A.:
- Property Let: 数値・文字列・日付などの 値型
- Property Set: オブジェクト(Range、Worksheet、カスタムクラス等)
' Property Let(値型)
product.Price = 1000
' Property Set(オブジェクト)
Set emp.Manager = mgrObject
Q4. Initialize/Terminate は必須ですか?
A. 任意 です。
必要な場合:
- リソース初期化(ファイル・DB接続)
- デフォルト値設定
- 自動採番
不要な場合:
- 単純なデータ保持クラス
- プロパティ設定で十分な場合
Q5. クラスモジュールをコピーして他のブックで使えますか?
A. 可能 です。
- VBE のプロジェクトエクスプローラーでクラスモジュールを右クリック
- ファイルのエクスポート (.cls ファイル保存)
- 他のブックで ファイルのインポート
Q6. クラス同士で相互参照できますか?
A. 可能ですが 循環参照に注意。
' Employeeクラス
Private mDepartment As Department
' Departmentクラス
Private mManager As Employee
過度な相互参照は設計を複雑にするため、必要最小限に。
Q7. 標準モジュールとクラスモジュールを併用すべきですか?
A. YES。適材適所で使い分けます。
- 標準モジュール: エントリーポイント(ボタンクリック等)、汎用ユーティリティ関数
- クラスモジュール: ビジネスロジック、データモデル
' 標準モジュール: エントリーポイント
Sub Main()
Dim emp As New Employee
emp.Name = "田中太郎"
emp.Age = 30
emp.Introduce
Set emp = Nothing
End Sub
まとめと次のステップ
本記事のまとめ
✅ クラスモジュールはデータと処理をひとまとめにする設計図
✅ Property Get/Let/Set でプロパティを実装
✅ Public Sub/Function でメソッドを実装
✅ Initialize/Terminate イベントで初期化・終了処理
✅ Collection/Dictionary で複数インスタンスを管理
✅ 可読性・保守性・再利用性が劇的に向上
✅ 大規模プロジェクトでは必須スキル


コメント