エクセルVBA オブジェクト指向備忘録 デザインパターン ファクトリー


202401015エクセルVBA オブジェクト指向備忘録 デザインパターン ファクトリー

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

(素人なので間違っているかもしれません。汗。)

ここ最近、クラス、オブジェクト指向をやってきて、思ったこととしたことの?備忘録(未確認・未確定メモ)です。
(素人なので間違っているかも。汗)

最近、VBAで、OOPオブジェクト指向プログラミング)について、意識しながら、コードを書いているのですが、今回も自分用のメモを書きます。


今回は、「ファクトリー」です。
でも、正しい使い方かは分かりません。汗。


<前知識>
前回同様youtubeで、「独り言のプログラミング」というのを見ました。
C#に関するものですが、分かりやすい気がします。感謝。
(不都合があれば、削除いたします。)
その動画の中でファクトリーに関して、下記のような説明がありました。
(少し変えてます。)

 


もとに、A→B(リンゴと表示)というコードがあり、
それを利用して、→C(みかんと表示)するコードを作成する感じでした。
(ただし、後から追加設定するのではなく、設計当初から考えられるのがファクトリーだとか。)

で、自分にとって、新鮮だったのは、A’の存在です。
(同一のAからのBとCへの枝分かれではなかったです。)


動画で紹介されていたのは、C#なので、よく分かっていませんが、
クラスAがあり、
クラスA’(AEx)は、クラスAを継承。

クラスB、クラスCをインターフェース化するITectを用意。


クラスB、クラスCをインターフェース化。


クラスAでは、クラスBをインスタンス化して、リンゴを表示。
(クラスAは、ITestを作動させるため、virtualとか少し記入多い。)


クラスA’(AEx)では、クラスBをインスタンス化して、みかんを表示。

大元のコードで、下記のように、ClassAExを呼べば、みかんが表示される仕組みです。
private void button1_Click(object sender, EventArgs e){
  ClassAEx obj = new ClassAEx();
  obj.print();
}


図ではこんな感じ。


分かりやすい。
(余談:ただ、ファクトリーは、実際には、あまり使われないとのことでした。
上記では、Aの中でBをnewしてますが、Aの中でnewするのをやめて、Aが生成される前に、BやCを渡す方法もあるからだそうです。)

 

 

あと、何となく分かっていないのが、最後の方の説明で、
factory.getinstance(1)という感じで、引数を渡すやり方は、ファクトリーに含むか含まないか、現場によって違うような話がありました。
これは、実際には、内部で、
if(type=1)return new classB()
というようなif文を隠しているだけとのこと。
この使い方がそんなに気になるのか、自分には分かりませんでした。

 

<本番:VBAではどうなる?>
chatGPT4oさんに聞きました。

●最初に作成してもらったコード
プロンプトに、
factory.getinstance(1)
factory.getinstance(2)
等例示を入れました。


コードは下記です。
=============================
' ClassA.cls
Option Explicit

Public Sub ShowMessage()
    MsgBox "これは ClassA のインスタンスです。"
End Sub

============================
' ClassB.cls
Option Explicit

Public Sub ShowMessage()
    MsgBox "これは ClassB のインスタンスです。"
End Sub

==========================
' Factory.cls
Option Explicit

Public Function GetInstance(ByVal TypeID As Integer) As Object
    Select Case TypeID
        Case 1
            Set GetInstance = New ClassA
        Case 2
            Set GetInstance = New ClassB
        Case Else
            MsgBox "無効なタイプIDです。"
            Set GetInstance = Nothing
    End Select
End Function

============================
' Module1.bas
Option Explicit

Sub TestFactoryPattern()
    Dim factory As Factory
    Dim obj As Object
    
    ' Factoryクラスを初期化
    Set factory = New Factory
    
    ' TypeID = 1 の場合は ClassA、2 の場合は ClassB を取得
    Set obj = factory.GetInstance(1)  ' ClassAのインスタンス取得
    If Not obj Is Nothing Then obj.ShowMessage
    
    Set obj = factory.GetInstance(2)  ' ClassBのインスタンス取得
    If Not obj Is Nothing Then obj.ShowMessage
End Sub
====================================
上記でいいのかなと思ったのですが、「あれ?implements」がないなとか、
図にしたら、クラスA’(に当たるもの)がないな)とか思いました。

上記コードの図(多分)

youtubeで、「独り言のプログラミング」さんで、「Factoryではないかも・・・」というのは上記のことなのだろうか。
(・・・違う気がする。)


●次に作成してもらったコード
プロンプトは、「VBAで、デザインパターンのファクトリーの簡単なコードを書いて。」

で、できたのは下記のコードです。

==============================
' Animal.cls
Option Explicit

Public Sub Speak()
    ' 各サブクラスで実装するべきメソッド
    MsgBox "This animal has no sound."
End Sub
=============================

' Dog.cls
Option Explicit
Implements Animal

Private Sub Animal_Speak()
    MsgBox "ワンワン! (Woof Woof!)"
End Sub
=============================

' Cat.cls
Option Explicit
Implements Animal

Private Sub Animal_Speak()
    MsgBox "ニャーニャー! (Meow Meow!)"
End Sub
=============================

' AnimalFactory.cls
Option Explicit

Public Function CreateAnimal(ByVal animalType As String) As Animal
    Select Case animalType
        Case "Dog"
            Set CreateAnimal = New Dog
        Case "Cat"
            Set CreateAnimal = New Cat
        Case Else
            MsgBox "Invalid animal type."
            Set CreateAnimal = Nothing
    End Select
End Function
============================

' Module1
Option Explicit

Sub TestFactoryPattern()
    Dim factory As New AnimalFactory
    Dim animal As Animal

    ' 犬のインスタンスを生成
    Set animal = factory.CreateAnimal("Dog")
    If Not animal Is Nothing Then animal.Speak

    ' 猫のインスタンスを生成
    Set animal = factory.CreateAnimal("Cat")
    If Not animal Is Nothing Then animal.Speak

    ' 存在しない動物を指定
    Set animal = factory.CreateAnimal("Bird")
    If Not animal Is Nothing Then animal.Speak
End Sub

==========================
よくある動物のimplementsを使ったコードです。

図にすると下記の感じ。(縦にしました。)

今度のは、implementsで、DogとCatができるから、一番最初の例のクラスAとクラスA’(AEx)
と考えていいのか?
良くわかなない。

ただ、Dogクラスの下に、クラスC
Catクラスの下にクラスDを持って来て、全体をファクトリーパターンだということはできるかも。


あまりしっくりこないが、深追いはやめる。そのうち分かるかも。


202401015エクセルVBA オブジェクト指向備忘録 デザインパターン ファクトリー追記

追記 さんざん書いたけど、上記コードはyoutube「独り言のプログラミング」様のC#のコードの再現ではなく、そもそも違っているようです。
間違いや変な引用の仕方で申し訳ない。自分用のメモということで許して下さい。

下記のコードが書き替えに近いか。

====================
'標準module

Sub Button1_Click()
    Dim obj As ClassAEx
    Set obj = New ClassAEx
'    Dim obj As ClassA
'    Set obj = New ClassA
    obj.TPrint
End Sub


' クラスモジュール名: ClassA
Option Explicit

Public Sub TPrint()
    Dim obj As ITest
    Set obj = Me.Create()
    MsgBox obj.GetMsg()
End Sub

Public Function Create() As ITest
    ' デフォルトでClassBのインスタンスを返す
    Set Create = New ClassB
End Function

' クラスモジュール名: ClassAEx
Option Explicit
' ClassAを拡張したクラス

Public Sub TPrint()  '追加
    Dim obj As ITest
    Set obj = Me.Create  'Meを修正
    MsgBox obj.GetMsg()
End Sub

 Function Create() As ITest  'private修正
    ' ClassCのインスタンスを返す
    Set Create = New ClassC
End Function


' クラスモジュール名: ClassB
Option Explicit

Implements ITest

Private Function ITest_GetMsg() As String
    ITest_GetMsg = "りんご"
End Function


' クラスモジュール名: ClassC
Option Explicit

Implements ITest

Private Function ITest_GetMsg() As String
    ITest_GetMsg = "みかん"
End Function


' クラスモジュール名: ITest
Option Explicit

Public Function GetMsg() As String
    ' 継承先で実装する
End Function
==================

これだと、この図の仕組み通りです。(継承はできていないけど。)
(ラップしていないといえばいいのかな。)