適材適所

パソコン作業の自動化・効率化のための情報を発信するブログ(VBA,PowerShellなど)

挫折した人のためのVBAクラスモジュール入門_その7

この記事では

  • VBAのクラスモジュールの使い方を解説しています。
  • 前回に引き続き、シートの情報をクラスモジュールを使ってオブジェクト化していく手法を説明しています。
  • 前回までのコードを整理しています。
  • この記事はクラスモジュールの実践的な使い方を学びたい人に向けて書いています。
  • シリーズものです。その1はこちら→挫折した人のためのVBAクラスモジュール入門_その1 - 適材適所

今回は具体的な話を

前回はプロパティのカプセル化とインターフェースの統一化についてでした。

抽象的な話になってしまったので、具体的な話に戻していきます。

今回は、GodExcelオブジェクトのプロパティの定義をクラスモジュールにやっていきたいと思います。

神エクセルの仕様

GoeExcelオブジェクトのプロパティについて考えるにあたり、改めて神エクセルがどんなものだったか再掲します。 f:id:shinmai_papa:20190711144335p:plain
そうそう、こんなワークシートでした。

改めて見ると、やっぱり酷いですね・・・。

シートの情報としては、こんな感じになっております。

名前フリガナ B6~U6
名前漢字   B8~U8
生年月日年  B12~E12
生年月日月  F12~G12
生年月日日  H12~I12
郵便番号1  B16~D16
郵便番号2  F16~I16
住所     B18~U19
メールアドレスB22~U23
勤務先名称   B26~N26
職業      P26~U26

GodExcelオブジェクトは、これらの情報を持つオブジェクトとしたいのでした。

これまでのProperty Let/Getを使うとこんな風にクラスを定義できます。

ちょっと長いです。

'GodExcel.cls

'プロパティ
Private pNameFurigana As String
Private pNameKanji As String
Private pBirthDay As Date
Private pPostalCode As String
Private pAddress As String
Private pMailAddress As String
Private pWorkPlace As String
Private pProffsion As String

Public Property Get NameFurigana() As String
   NameFurigana = pNameFurigana
End Property

Public Property Let NameFurigana(aNameFurigana As String)
   pNameFurigana = aNameFurigana
End Property

Public Property Get NameKanji() As String
   NameKanji = pNameKanji
End Property

Public Property Let NameKanji(aNameKanji As String)
   pNameKanji = aNameKanji
End Property

Public Property Get BirthDay() As Date
   BirthDay = pBirthDay
End Property

Public Property Let BirthDay(aBirthDay As Date)
   pBirthDay = aBirthDay
End Property

Public Property Get PostalCode() As String
   PostalCode = pPostalCode
End Property

Public Property Let PostalCode(aPostalCode As String)
   pPostalCode = aPostalCode
End Property

Public Property Get Address() As String
   Address = pAddress
End Property

Public Property Let Address(aAddress As String)
   pAddress = aAddress
End Property

Public Property Get MailAddress() As String
   MailAddress = pMailAddress
End Property

Public Property Let MailAddress(aMailAddress As String)
   pMailAddress = aMailAddress
End Property

Public Property Get WorkPlace() As String
   WorkPlace = pWorkPlace
End Property

Public Property Let WorkPlace(aWorkPlace As String)
   pWorkPlace = aWorkPlace
End Property

Public Property Get Proffsion() As String
   Proffsion = pProffsion
End Property

Public Property Let Proffsion(aProffsion As String)
   pProffsion = aProffsion
End Property

次に、このクラスからオブジェクトを生成して、そのオブジェクトに値をセットして出力するプログラムはこんな感じでしょうか。

Const NAME_FURI_RNG As String = "B6:U6"
Const NAME_KAN_RNG As String = "B8:U8"
Const BIRTH_Y_RNG As String = "B12:E12"
Const BIRTH_M_RNG As String = "F12:G12"
Const BIRTH_D_RNG As String = "H12:I12"
Const POST1_RNG As String = "B16:D16"
Const POST2_RNG As String = "F16:I16"
Const ADR_RNG As String = "B18:U19"
Const MAIL_RNG As String = "B22:U23"
Const WORK_PLACE_RNG As String = "B26:N26"
Const PROFFESION_RNG As String = "P26:U26"

Sub sample()
   Dim god As GodExcel
   Set god = New GodExcel
   '値を設定
   god.NameFurigana = getItem(Range(NAME_FURI_RNG))
   god.NameKanji = getItem(Range(NAME_KAN_RNG))
   god.BirthDay = DateSerial(getItem(Range(BIRTH_Y_RNG)), _
   getItem(Range(BIRTH_M_RNG)), _
   getItem(Range(BIRTH_D_RNG)))
   god.PostalCode = getItem(Range(POST1_RNG)) & getItem(Range(POST2_RNG))
   god.Address = getItem(Range(ADR_RNG))
   god.MailAddress = getItem(Range(MAIL_RNG))
   god.WorkPlace = getItem(Range(WORK_PLACE_RNG))
   god.Proffsion = getItem(Range(PROFFESION_RNG))

    'イミディエイトウィンドウに出力
   Debug.Print god.NameFurigana
   Debug.Print god.NameKanji
   Debug.Print god.BirthDay
   Debug.Print god.PostalCode
   Debug.Print god.Address
   Debug.Print god.MailAddress
   Debug.Print god.WorkPlace
   Debug.Print god.Proffsion
End Sub
'ヘルパー関数
Private Function getItem(aRng As Range) As String
   Dim recString As String
   Dim i As Long
   For i = 1 To aRng.Count
      Dim r As Range: Set r = aRng.Item(i)
      recString = recString & r.Value
   Next i
   getItem = recString
End Function

これは・・・。

どうでしょう?標準モジュールで値をセットしているこのコード・・・。

これじゃない感がすごいです。

もっと、こう、なんというんでしょう、オブジェクトを生成した時点で、オブジェクトは値を知っていて、それを出力するだけってのが本当の理想だと思います。

これでは、オブジェクトへの値の設定の責任はオブジェクトの外にある状態です。

値の設定までの責任をオブジェクトに持たせるためには、オブジェクトが生成される時点で自動的に呼ばれる、コンストラクタと呼ばれるものにその処理を定義してあげます。

コンストラクタを定義する

VBAのクラスモジュールでコンストラクタを定義するためには、次の手順で行います。

まず、VBEのコードエディタの上部のタブをクリックし、Classを指定します f:id:shinmai_papa:20190804163931p:plain

次にその隣をInitalizeに設定すると、Class_Initializeサブプロシージャが生成されます。 f:id:shinmai_papa:20190804163935p:plain

このClass_Initalizeサブプロシージャは特別なサブプロシージャで、オブジェクトが生成されるときに必ず呼ばれます。

GodExcelオブジェクトの例で言うと、オブジェクト生成時にここで値を全て設定してあげれば、あとは設定する手間が省けます。

そうすることで、GodExcelオブジェクトを外部から呼び出すプログラムは、GodExcelオブジェクトを生成するだけで、値を取得することができます。

また、ワークシートのどの範囲がどの値なのかを外部のプログラムは意識する必要がありません。

これでワークシートの位置情報がカプセル化されている状態になり、ワークシートのセル範囲を変更した際は、このクラスの定義だけを変更すれば他のプログラムには影響が出ません。

さきほどの標準モジュールに定義したプログラムを使って、コンストラクタを定義してみます。

'コンストラクタ
Private Sub Class_Initialize()
  '定数の定義は省略
   pNameFurigana = getItem(Range(NAME_FURI_RNG))
   pNameKanji = getItem(Range(NAME_KAN_RNG))
   pBirthDay = DateSerial(getItem(Range(BIRTH_Y_RNG)), _
                                       getItem(Range(BIRTH_M_RNG)), _
                                       getItem(Range(BIRTH_D_RNG)))
   pPostalCode = getItem(Range(POST1_RNG)) & getItem(Range(POST2_RNG))
   pAddress = getItem(Range(ADR_RNG))
   pMailAddress = getItem(Range(MAIL_RNG))
   pWorkPlace = getItem(Range(WORK_PLACE_RNG))
   pProffsion = getItem(Range(PROFFESION_RNG))
End Sub
'ヘルパー関数
Private Function getItem(aRng As Range) As String
   Dim recString As String
   Dim i As Long
   For i = 1 To aRng.Count
      Dim r As Range: Set r = aRng.Item(i)
      recString = recString & r.Value
   Next i
   getItem = recString
End Function

これで、GodExcelオブジェクトが生成された時点で、値が設定されているはずです。

いざテスト!!

テストしてみる

テスト対象のワークシートはこうしました。 f:id:shinmai_papa:20190804163940p:plain

標準モジュールはオブジェクトを生成して、その値を出力すだけにします。

Sub sampleTest()
   Dim god As GodExcel
   Set god = New GodExcel
   Debug.Print god.NameFurigana
   Debug.Print god.NameKanji
   Debug.Print god.BirthDay
   Debug.Print god.PostalCode
   Debug.Print god.Address
   Debug.Print god.MailAddress
   Debug.Print god.WorkPlace
   Debug.Print god.Proffsion
End Sub

実行結果(イミディエイトウィンドウ)

テスト
手酢徒
2000/01/01
1000004
東京都千代田区大手町1-1
sample@sample.co.jp
サンプル株式会社
営業

おお、これです、これ!

標準モジュールがキレイです!!

標準モジュールでは、ただGodExcelオブジェクトを生成しているだけです。

GodExcelオブジェジェクト内部で何が行われているかは呼び出し元であるsampleTestは知らない状態です。

知っているのは外部に公開されているプロパティだけです。

つまり、GodExcelオブジェクトの中にシートの情報を隠蔽(カプセル化)することができました。

なんだかいい感じのオブジェクトになってきました。これぞクラスモジュールを使っている感がありますね。

これでGodExcelオブジェクトもかなりレベルアップしました。Lv30くらいですねかね。

シートの情報は構造化され、GodExcelオブジェクトを通して情報を知ることができるようになりました。

また、セルの位置が変わっても、変更箇所はこのGodExcelオブジェクトを定義するクラスモジュールだけで済みます。

あと少しでGodExcelオブジェクトも完成し、神になれます、きっと。

今回は盛沢山でした。コンストラクタについて早足すぎましたので、一旦GodExcelオブジェクトから離れて、次回はもうコンストラクタの話をしたいと思います。

ここまでお読みいただきありがとうございました。

www.tekizai.net