エクセルVBA オブジェクト指向備忘録クラス001(クラスの書き方の比較)

20240122エクセルVBA オブジェクト指向備忘録クラス001


エクセルVBA オブジェクト指向備忘録(クラスの書き方の比較)

エクセルVBAオブジェクト指向、初心者です。
VBA自体は、以前から使用していました。
オブジェクト指向というものがあることは、知っていましたが、ネットなどを見てもよく分からず、挫折した過去あり。

エクセルVBAで、オブジェクト指向を必要とすることもなかったので、諦めていましたが、
youtubeで、「ひとりごとワンダーランド」様のエクセルVBAでのクラスに関する動画を見させていただき、
少しだけやる気になっています。感謝。
(まだ、動画は最後まで見ていません。汗)


で、「ひとりごとワンダーランド」様が強調していることの一つに、スコープ「public」と「private」についての使い方がありました。

よく変数の宣言に、publicを使っているものが散見されるが、privateがいいのではないかとの意見。
そうしないと、変数がどこからでも書き換えられて、クラスにした意味(意義)が半減するような指摘がありました。
カプセル化したのに、意味がない。)


で、並行して、ネットHPで「いつも隣にITのお仕事」様も見ている。
(これも途中までしか見てません。汗)
(注:まだ途中です。最後まで読んだら、もしかして、ここに書いた内容(捉え方・考え方)が変わるかもしれません。)(自分用メモなのでご容赦下さい。)

 

愚弟的には、「いつも隣にITのお仕事」様の方は、「エクセルVBAでクラスモジュールを使って独自のコレクションを作る方法」まで見ています。
そこでは、下記のように、プログラムを書いています。
(もし不都合ありましたら、削除致します。)
一部、private変数(Id)もありますが、基本的にpublic変数を使用しています。
(注:このページは、独自のコレクションを使うと書いてあるように、初心者には、理解がややこしいトコです。クラスを新たに作ってその中にコレクションを置く・・・。)


標準モジュール//////////////////////①
Sub Mysub()

    Dim myPersons As Persons
    Set myPersons = New Persons
    Set myPersons.Items = New Collection   

    With Sheet1
    
        Dim i As Long
        i = 2
        
        Do While .Cells(i, 1).Value <> ""
            Dim p As person       
            Set p = New person
            
            p.Initialize .Range(.Cells(i, 1), .Cells(i, 4))
            myPersons.Items.Add p, p.Id

            i = i + 1
        Loop
        
    End With

    Stop
    
End Sub


クラスPerson///////////////////////②(②は今回のテーマとは関係ありません)
Private Id_ As String   'privateにしている。他の変数はpublic

Public FirstName As String

Public Gender As String

Public Birthday As Date

Public Sub Initialize(ByVal rng As Range)

    Id_ = rng(1).Value
    FirstName = rng(2).Value
    Gender = rng(3).Value
    Birthday = rng(4).Value

End Sub

Public Property Let Id(ByVal newId As String)
    If Id_ <> "" Then                                'もしif文なければ、何回もアクセス可能
        Debug.Print "Idは上書きすることはできません"
    Else
        Id_ = newId
    End If
End Property

Public Property Get Id() As String

    Id = Id_

End Property

 

クラスPersons/////////////////////////////③
Public Items As Collection

 

①②③の構成になっている。特に③にpublicを使っている。
なお、「いつも隣にITのお仕事」様は、お作法として、クラス内のprivate変数は、「Id_」のように、後ろにアンダーバーを入れています。
インスタンスに使う変数はpなどでシンプルです。

これだけで、クラスPersonsのItemsがコレクションとして使用可能になっている。※
(※この章では、コレクションを新たなクラス(クラス②)で作って、オブジェクトとして使用するというややこしいことをしています。)

 


**********************************************************
上記を、「ひとりごとワンダーランド」様のprivate変数を多用したパターンに書き換える。
(自分で考えたので、間違っていたらスミマセン。)

標準モジュール//////////////////////①
Sub Mysub()
    
    Dim vPersons As Persons  '独自クラス
    Set vPersons = New Persons 'クラス生成
   
    With Sheet1
        Dim i As Long
        i = 2
        Do While .Cells(i, 1).Value <> ""
            Dim vPerson As person
            Set vPerson = New person  '毎回セット
            vPerson.Initialize .Range(.Cells(i, 1), .Cells(i, 4))
            
            vPersons.Items.Add vPerson, vPerson.Id    
            
            i = i + 1
        Loop
   End With

    Stop
End Sub


クラスPerson///////////////////////②(②は今回のテーマとは関係ありません)
Private mId As String
Private mFirstName As String
Private mGender As String
Private mBirthday As Date

Public Sub Initialize(rng As Range)  'クラスinitializeではなくメソッドで初期化
                                    '(VBAのクラスinitializeは、(コンストラクタ)引数持てないらしい?)
    mId = rng(1).Value
    mFirstName = rng(2).Value
    mGender = rng(3).Value
    mBirthday = rng(4).Value

End Sub


Property Get Id() As String  

    Id = mId      

End Property

Property Get FirstName() As String    
    FirstName = mFirstName    
End Property


Property Get Gender() As String   

    Gender = mGender     

End Property


Property Get Birthday() As Date   

    Birthday = mBirthday    

End Property

 

 

Property Let Id(pId As String)

    If mId <> "" Then
        Debug.Print "IDは上書き不可"
    Else
         mId = pId
    End If

End Property


Property Let FirstName(pFirstName As String)

     mFirstName = pFirstName

End Property


Property Let Gender(pGender As String)

     mGender = pGender

End Property

Property Let Birthday(pBirthday As Date)

     mBirthday = pBirthday

End Property

 

 

クラスPersons/////////////////////////////③
Private mItems As Collection   'privateにする

Private Sub Class_Initialize()
    Set mItems = New Collection
End Sub


Property Set Items(pItems As Collection)
    Set mItems = pItems
End Property


Property Get Items() As Collection
    Set Items = mItems
End Property

**********************************************************
上記が、「ひとりごとワンダーランド」様風のprivate変数を多用したパターン。
(違っていたらすみません。)
「ひとりごとワンダーランド」様は、お作法として、基本的に、標準モジュールの変数は、
Set vPersons = New Personsのように、vを付ける。

また、クラス内変数等は、
Property Let Id(pId As String)
         mId = pId
End Property
のように、渡ってくる変数にはp、
クラス内変数は、mを付けている。

で、通常、変数をprivateでやり取りしようとすると、
letやgetが必要だが、
今回は、クラス内で、コレクションを扱うため、
setやget以外に、
Private Sub Class_Initialize()
    Set mItems = New Collection
End Sub
というように、Class_initializeで、コレクションの定義を行った。

 

で、いいのかな・・・。汗

 

追加メモ:(間違っているかも)
propertyは、関数のようなものなのだろう。
mainでクラスが作られて、呼ばれれば必ずそこを通る。
関数っぽいので色んなところから呼ばれても、if文などで、書き換えに制限を加えることができる。

 

今まで自分は、「手続き型」のコードしか書いてこなかったので、クラスには違和感あるし、今まで作ってきたものをリファクタリングするほど、手慣れることもなさそうだが、違った発想に感じられるので、面白い気もします・・・汗。

 

追加メモ:(間違っているかも)
propertyは、関数のようなものなのだろう。

mainでクラスが作られて、呼ばれれば必ずそこを通る。
関数っぽいので色んなところから呼ばれても、if文などで、書き換えに制限を加えることができる。