【ExcelVBA・マクロ】クラスモジュール入門【オブジェクト指向でコードを劇的に整理!実例15パターンで完全マスター】

クラスモジュール入門 ExcelVBA

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
' → 他のコードに影響なし!

最も簡単なクラスを作る

クラスモジュールの追加方法

  1. VBE(Visual Basic Editor)を開く(Alt + F11)
  2. 挿入クラスモジュール
  3. プロパティウィンドウ(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_Pricem は member の略
PropertyPrice大文字始まり
引数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変数 + PropertyPublic変数
データ検証✔ できる✗ できない
読み取り専用✔ 可能✗ 不可
将来の変更✔ 内部実装変更可能✗ 全コード修正必要
カプセル化✔ 保護される✗ 直接アクセス

例外: 簡易なクラス・プロトタイプ開発では 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. 可能 です。

  1. VBE のプロジェクトエクスプローラーでクラスモジュールを右クリック
  2. ファイルのエクスポート (.cls ファイル保存)
  3. 他のブックで ファイルのインポート

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 で複数インスタンスを管理
✅ 可読性・保守性・再利用性が劇的に向上
✅ 大規模プロジェクトでは必須スキル

スポンサーリンク
スポンサーリンク
ExcelVBA
いがぴをフォローする

コメント

タイトルとURLをコピーしました