エクセルVBA オブジェクト指向備忘録 デザインパターン(の前に委譲と継承)メモ

202401015エクセルVBA オブジェクト指向備忘録 デザインパターン(の前に委譲と継承)メモ

 

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

 

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

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

先日、GOFと呼ばれる人のデザインパターンで、「アダプター」(的なもの?)について、書きました。

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


<次回以降、もっと詳しく書くための、「頭整理」メモです。>
今まで、委譲とか継承とか、implementsとか、witheventsとか、何となく使えるモノは使っていましたが、
デザインパターンを考慮するに当たり、それらとの関係を現時点で整理?するメモです。

 


<継承と委譲について>(便宜上の覚え)(後日変わるかもしれません。)
VBAでは、継承≒インプリメント」と覚えておこうと思う。
↑そもそもVBAでは継承はありません。
だから、(多分)メソッドに関しては、implementsを使って、代用するのがいいのかも。
で、ここで覚えておくのは、implementsは「委譲」ではなく”継承もどき”として使うということ。
(プロパティについての継承は、「もっとできない」。無理するなら、親クラスを子クラスの中でインスタンス化することができるが、ここでは書かないです。)

あと、youtubeで、「独り言のプログラミング」というのを見ました。
C#に関するものですが、分かりやすい気がします。感謝。
(不都合があれば、削除いたします。)
その動画の中で思ったのですが、「オーバーライド」と言ったら「継承している箇所」と認識することにします。
VBAではオーバーライドもないですが、他の言語から、OOPを勉強する場合の知識として覚えておきます。)

●委譲について・・・(便宜上の覚え)
「委譲は、implements使わない」。「委譲は、下位のクラスに委ねて渡すイメージ。」
この定義は自分用です。

chatGPT4oさんに、コード書いてもらいました。

==========================
printer.cls
' Printerクラスは印刷処理を実行する
Option Explicit

Public Sub PrintContent(content As String)
    MsgBox "印刷中: " & content
End Sub
===========================
DocumentManager.cls
' DocumentManagerクラスはPrinterに印刷を委譲する
Option Explicit

Private printer As Printer

Public Sub Initialize()
    Set printer = New Printer  ' Printerオブジェクトを初期化
End Sub

Public Sub PrintDocument(docContent As String)
    printer.PrintContent docContent  ' Printerに印刷を委譲する
End Sub
=========================
モジュール
Sub TestDelegation()
    Dim docManager As DocumentManager
    Set docManager = New DocumentManager

    docManager.Initialize  ' Printerの初期化
    docManager.PrintDocument "テスト用のドキュメント"  ' 印刷を移譲
End Sub
======================

図だと、こんなイメージです。「委譲は、implements使わない」とは、条件分岐をさせない「1対1」(親:子)のイメージです。
chatGPT4oさんに、「1対多の委譲」について聞くと、結局、implemntsを使った”継承もどき”を教えてくれるので、
ここでは、委譲は、1対1で、「implements使わない」を使わないシンプルな方法と覚えておきます。

余談:「委譲」と「移譲」について(漢字違い)
@tatsumi_t2さんの「【オブジェクト指向】移譲じゃないよ、委譲だよ」(qiita)という記事で分かりました。
漢字の違いですが、移譲の方は、「元のクラスを放棄して、他のクラスに移してしまう」感じでした。

●継承について・・・(便宜上の覚え)
「継承は、implements使う」。「継承は、インターフェース。窓口のイメージ。」
「下位から見ると、(移譲と同じように)引き継ぐイメージだが、全体・上から見ると”枝分かれ””分岐処理”がある。」
この定義は自分用です。

chatGPT4oさんに、コード書いてもらいました。

================================
IPrinter.cls (インターフェース)
' インターフェースとして動作するクラス
Option Explicit

' 共通のメソッドを定義
Public Sub PrintContent(content As String)
End Sub
=================================
TextPrinter.cls (テキスト印刷用クラス)
Option Explicit
Implements IPrinter  ' インターフェースを実装

' IPrinterのメソッドを具体的に実装
Private Sub IPrinter_PrintContent(content As String)
    MsgBox "テキスト印刷: " & content
End Sub
=================================
PdfPrinter.cls (PDF印刷用クラス)
Option Explicit
Implements IPrinter  ' インターフェースを実装

' IPrinterのメソッドを具体的に実装
Private Sub IPrinter_PrintContent(content As String)
    MsgBox "PDF印刷: " & content
End Sub
===================================
module
Sub TestInterfaceImplementation()
    Dim printer As IPrinter  ' インターフェース型で宣言

    ' TextPrinterを使う場合
    Set printer = New TextPrinter
    printer.PrintContent "これはテキスト用の文書です"

    ' PdfPrinterを使う場合
    Set printer = New PdfPrinter
    printer.PrintContent "これはPDF用の文書です"
End Sub
===============================

図だと、こんなイメージです。
上記のように使い分けようかな・・・と思っていますが・・・

 

 

//////////////////////////////////
最後に分からなくなる・・・?。
上記のように、考えていたのですが、
chatGPT4oさんに、「「委譲」の方法で、分岐処理できますか?」
と聞いてみたところ、下記のコードを教えてくれました。


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

' 各プリンターが実装すべきメソッドの宣言
Public Sub PrintDocument(ByVal content As String)
    ' 何もしない(サブクラスで実装される)
End Sub


' === クラスモジュール名: PDFPrinter ===
Option Explicit
Implements IPrinter

' IPrinterインターフェースのメソッドを実装
Private Sub IPrinter_PrintDocument(ByVal content As String)
    MsgBox "PDFプリンターで印刷: " & content
End Sub


' === クラスモジュール名: TextPrinter ===
Option Explicit
Implements IPrinter

' IPrinterインターフェースのメソッドを実装
Private Sub IPrinter_PrintDocument(ByVal content As String)
    MsgBox "テキストプリンターで印刷: " & content
End Sub


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

' 現在のプリンターを保持する変数
Private currentPrinter As IPrinter

' 使用するプリンターを設定するメソッド
Public Sub SetPrinter(ByVal printer As IPrinter)
    Set currentPrinter = printer
End Sub

' ドキュメントを印刷するメソッド(責務を委譲)
Public Sub Print(ByVal content As String)
    If Not currentPrinter Is Nothing Then
        currentPrinter.PrintDocument content
    Else
        MsgBox "プリンターが設定されていません。"
    End If
End Sub


Sub TestPrinterDelegation()
    Dim manager As PrinterManager
    Set manager = New PrinterManager
    
    Dim pdfPrinter As PDFPrinter
    Set pdfPrinter = New PDFPrinter
    
    Dim textPrinter As TextPrinter
    Set textPrinter = New TextPrinter

    ' PDFプリンターに委譲して印刷
    manager.SetPrinter pdfPrinter
    manager.Print "PDF用のドキュメント"

    ' テキストプリンターに委譲して印刷
    manager.SetPrinter textPrinter
    manager.Print "テキスト用のドキュメント"
End Sub

図にするとこんな感じ。
上記の説明で言うと、委譲&継承もどき(implements)か。
これがよく使うパターンかも。