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)か。
これがよく使うパターンかも。