クラスを使ったコードと使わないコードを比較する
クラスについて勉強してみたのですがいざ実践となるとどうしたら良いのか考えてしまった事はありませんか?
クラスを用いたコードを実装するにあたりサンプルが欲しいなと考えている方に向けて回答を用意しました。
クラスを勉強しただけではコードを用意するのは難しいのでサンプルコードを用意しました。
自力でClassモジュールを使ったプログラムを実装するには相当なトレーニングが必要です。
ただクラスモジュールを使ったコードを用意するだけだと理解するのが難しい事が予想されます。
比較対象として同じアウトプットでクラスを使わないコードも用意しました。一緒に見る事で理解につながります。
今回の記事はコードだけで相当な量になります。解説はポイントだけにしました。需要次第で別記事を書きます。
関連書籍
クラスについて非常に詳しく解説している書籍です。
クラスだけではなくVBAを体系的に学びたいという方にはおすすめできる本です。
電子書籍(Kindleストア)はこちらです。私一部の書籍は電子書籍で購入しています。
電子書籍をVBEと同じ画面で横に並べながら作業する事で作業効率が劇的にあがります。
クラスとは(クラスモジュールとは)
こちらの記事で説明しています。
クラスを用意する為の手順を解説VBA|クラスとは何か|本当に難しいのかを3つのポイントから検証
サンプルコード
コードだけ説明しても伝わりにくい事が予想されます。まずは実装したコードで何をするのかを確認しましょう。
今回用意したコードでは「任意の価格に対して状況に応じて税率毎の値を返す」という作業をしています。
任意の価格(動画では税抜価格1,000円)のものに対し税率10%、8%の税込価格が算出できるというツールです。
動画:サンプルコードのアウトプット
詳細は動画をご覧ください。(約2分です:BGMがありますので音にはご注意ください)
動画の補足説明
セルC4に任意の値(半角数値)を入力して右側の青いボタンを押下します。
上のボタンはクラスを使って用意したコードが関連付けられたボタンです。
下のボタンはクラスを使わないコードが関連付けられたボタンです。どちらも同じことができます。
青いボタンを押すといくつかのメッセージボックスが表示されます。
都度表示されたメッセージボックス内の質問に回答していくことで処理が変わる様にしてあります。
続いてクラスを使わないコードとクラスを使ったコードを紹介します。
クラスを使わないコード
先にクラスを使わないコードを紹介します。理由はこちらのコード+動画でやっている事を理解してほしい為です。
クラスを使ったコードではクラスの動きを追う事だけで精一杯になります。
最低でもコードの内容は理解してからクラス有のコードを見る流れにする為に先にクラス無のコードを出しました。
コード
標準モジュールに全作業を記入しています。1つのプロシージャに全てを書いているので理解し易いです。
こちらのコードは特に説明しません。(クラスを勉強される方なら問題ないですよね)
Option Explicit
Sub 会計_クラス未使用()
'***************************************************************************************
'変数を定義
Dim product_value As Long '税抜価格
Dim tax_rate As Single '税率
Dim ws As Worksheet 'ワークシート
Set ws = ActiveSheet
'***************************************************************************************
'税抜価格の数値の有無や型を確認し変数product_valueに値をセットする
With ws
If .Range("C4") = "" Or .Range("C4") = 0 Then
MsgBox "セルC4に半角数値で税抜価格を設定してください", vbCritical, "セルC4を見直してください"
Exit Sub
Else
If Not IsNumeric(.Range("C4")) Then
MsgBox "セルC4に半角数値で税抜価格を設定してください", vbCritical, "セルC4を見直してください"
Exit Sub
Else
'ここに落ちるとスルーして作業が進む
End If
End If
End With
product_value = Range("C4") 'セルから値を取得
'***************************************************************************************
'状況に応じて変数tax_rateに値をセットする
If MsgBox("店内飲食ですか?(税率10%)", vbYesNo + vbExclamation, "教えてください") = vbYes Then
tax_rate = 0.1 '店内飲食
Else
If MsgBox("持ち帰りですか?(税率8%)", vbYesNo + vbExclamation, "教えてください") = vbYes Then
tax_rate = 0.08 '持ち帰り
Else
'指定無しの時はメッセージボックスで再度質問への回答を促す
MsgBox "最初からやり直してください" & vbCrLf _
& "(店内飲食か持ち帰りのどちらかを選択してください)", vbCritical
Exit Sub
End If
End If
'***************************************************************************************
'各種結果を計算してメッセージボックスとして表示
MsgBox "税率" & tax_rate * 100 & "%で計算" & vbCrLf & vbCrLf _
& "税込価格:" & FormatCurrency(product_value * (1 + tax_rate)) & vbCrLf & vbCrLf _
& "商品価格:" & FormatCurrency(product_value) & vbCrLf & vbCrLf _
& "税金:" & FormatCurrency(product_value * tax_rate), vbInformation, "お知らせ"
'***************************************************************************************
End Sub
クラスを使ったコード
やっている事はクラスを使っていないコードと同じです。
任意の金額をセルに入力し店内飲食か持ち帰りを選択すると各税率を含めた金額が表示されるという建付けです。
ワークシートとVBAProject
まずはVBAProjectです。VBEのプロジェクトエクスプローラの画像を用意しました。
標準モジュール1つとクラスモジュール2つです。クラスを用意する方法はこちらの記事をご覧ください。
次はワークシートです。クラスを使わないコードの時と同じ仕様です。
セルC4に任意の金額を入力後セルE3付近にある会計(クラス使用)ボタンを押下します。
メッセージボックスの表示に従って操作を進めると税込価格などが表示されます。
コード
コードを貼り付ける場所は3箇所です。
- 標準モジュール
- Checkクラス
- Priceクラス
ワークシートのボタンは標準モジュールの「会計_クラス使用」プロシージャを関連付けてください。
ボタンにマクロを登録する方法はこちらをご覧ください。
標準モジュール(Module1)のコードです。
Sub 会計_クラス使用()
Dim myC As Check 'クラスを定義する
Set myC = New Check 'クラスをインスタンス化
myC.check_please
End Sub
Checkクラスのコードです。
Public Sub check_please()
Dim myP As Price 'クラスを定義する
Set myP = New Price 'クラスをインスタンス化
With myP
'Priceクラスで各種値を用意する
.initialize
If .calc_tax = 0 Then
Exit Sub
Else
'Priceクラスから必要な値を取り出してメッセージボックスに表示させる
MsgBox "税率" & .calc_tax & "%で計算" & vbCrLf & vbCrLf _
& "税込価格:" & FormatCurrency(.tax_include) & vbCrLf & vbCrLf _
& "商品価格:" & FormatCurrency(.product_value) & vbCrLf & vbCrLf _
& "税金:" & FormatCurrency(.tax_only), vbInformation, "お知らせ"
End If
End With
End Sub
Priceクラスのコードです。
Private wst As Worksheet
Private rte As Single
Private val As Long
Public Property Let input_ws(ByVal new_ws As Worksheet)
If wst Is Nothing Then
Set wst = new_ws 'ワークシートを設定
'Else
'MsgBox "シートは既に設定されているので変更しません", vbCritical
End If
End Property
Public Property Let input_tax(ByVal new_tax As String)
If rte = 0 Then
rte = new_tax
'Else
'MsgBox "税率は既に決まっているので変更しません", vbCritical
End If
End Property
Public Property Let input_value(ByVal new_value As Variant)
'製品価格
If IsNumeric(wst.Range("C4")) Then
If val = 0 Then
val = new_value
'Else
'MsgBox "税抜価格は既に決まっているので変更しません", vbCritical
End If
Else
'半角数値じゃなかったらスルー
End If
End Property
Public Property Get product_value() As Long
'製品価格
product_value = val
End Property
Public Property Get tax_include() As Currency
'税込価格を算出
tax_include = val * (1 + rte)
End Property
Public Property Get tax_only() As Currency
'税金のみを算出
tax_only = val * rte
End Property
Public Property Get calc_tax() As Currency
'税金の表示を確定させる(パーセント表示にする)
calc_tax = rte * 100
End Property
Public Sub initialize()
input_ws = ActiveSheet 'ボタンが用意されたシートを変数wsにセット
input_value = wst.Range("C4") 'セルC4の値を変数input_valueにセットする
If val = 0 Then
MsgBox "セルC4に半角数値で税抜価格を設定してください", vbCritical, "セルC4を見直してください"
Exit Sub
Else
'値が入っていれば次の処理に進む
End If
If MsgBox("店内飲食ですか?(税率10%)", vbYesNo + vbExclamation, "教えてください") = vbYes Then
input_tax = 0.1
Else
If MsgBox("持ち帰りですか?(税率8%)", vbYesNo + vbExclamation, "教えてください") = vbYes Then
input_tax = 0.08
Else
'指定無しの時はメッセージボックスで再度質問への回答を促す
MsgBox "最初からやり直してください" & vbCrLf _
& "(店内飲食か持ち帰りのどちらかを選択してください)", vbCritical
Exit Sub
End If
End If
End Sub
解説
この記事内で全部を説明するのは難しいです。よってここでは勉強する為のポイントを提示します。
- 「どんな構造でクラスを用意するのか」の考え方
- クラスを用意する方法(Newの使い方)
- クラスの中でプロパティとメソッドを用意する方法
- クラス内にPrivateで変数を用意する方法
- クラスの中にクラスを用意する方法(サブルーチン含む)
- Public Property Let、Public Property Get (Setはあとで勉強してください)
ひとまず以降に1番から6番まで簡単な解説を用意しておきます。
需要次第でそれぞれの項目にリンク(別記事)を用意して説明する事を予定しています。
1_「どんな構造でクラスを用意するのか」の考え方
今回は標準モジュール1つとクラスを2つ用意しています。理由はやる事を分けたかった為です。
主にデータの振る舞いをCheckクラスに用意しプロパティへの値を用意する行為をPriceクラスに用意しています。
- 1標準モジュールからCheckクラスを実体化し呼び出す
実体化されたCheckオブジェクト配下のcheck_pleaseメソッドを実行します
参考:ワークシートのボタンは標準モジュールと連動しています
- 2CheckクラスからPriceクラスを実体化し各プロパティに値を用意する様に指示
指示はPriceクラスのInitializeメソッドに集約しています
- 3PriceクラスではInitializeメソッドからの指示で変数への初期値入力
その後各プロパティへの値を代入します
- 4Checkクラスに戻りメッセージボックスからPriceクラスで用意された値を表示
Property Getプロシージャを使って値を揃えます
- 5標準モジュールに戻って処理が終了する
標準モジュールから始まった処理ですので最後は標準モジュールで終了となります
隠蔽の意味も込めて変数を代入したり計算するモジュールに直接アクセス出来ないような仕様にしました。
こういった構造でコードを用意するという思考はクラスを勉強しないと気付かない要素です。
正確にはサブルーチンの勉強をしても身に付くのですがクラスの方が自由度が高いのでより勉強になります。
クラスを勉強すると「どんな構造(環境)でコードを用意するのか」を考えることができる様になります。
2_クラスを用意する方法(Newの使い方)
こちらの記事で解説しています。
クラスを用意する為の手順を解説VBA|クラスとは何か|本当に難しいのかを3つのポイントから検証
3_クラスの中でプロパティとメソッドを用意する方法
2番と同様の記事で一部を紹介しています。
4_クラス内にPrivateで変数を用意する方法
どのモジュールでも共通事項ですがPrivateで変数を定義するとそのモジュールの中からのみ変数にアクセスできます。
今回のケースにおいてはCheckクラスから直接PriceクラスのPrivate変数を代入する事ができない様にしています。
よって画像のような流れでCheckクラスからPriceクラス内の3種類のPrivate変数に値を入力しています。
プロパティへの値の入力はPriceクラスにあるInitializeメソッドに任せています。
まずはCheckクラスからPriceクラスのInitializeメソッドに指示を出します。
その後最終的にはこの後解説するPublic Property Letプロシージャを使って変数に値を代入しています。
画像の赤矢印は指示が通りません。理由はPriceクラス内の変数にPrivateが付いているからです。
PriceクラスのPrivate変数に値を代入するにはPriceクラス内からの指示が必要です。
5_クラスの中にクラスを用意する方法
Checkクラスの中でPriceクラスを実体化させる事でCheckクラスからPriceクラスへのアクセスを実現させます。
この仕様を逆手にとって標準モジュールからPriceクラスにはアクセスできない様にしています。ご注意ください。
出来るだけ変数に値を用意するモジュールを隠蔽させたくてこの仕様にしました。
標準モジュールから見ると2階層下で変数を設定しています。VBAが分かってないと使えない仕様にしています。
6_Property Let、Property Get
簡単に申し上げますとLetで変数に値を入力、Getで変数の値を取り出すという仕様です。
Letで変数に値を入力する事でひと手間加える事ができます。
今回は値が無しだったりゼロの時だけ変数を入力できるようなコードにしました。
Getについてはメッセージボックスで表示する値を計算して出せる様に用意しました。
まとめ
同じアウトプットに対して「クラスを使ったコード」と「クラスを使わないコード」を用意してみました。
実際にワークシートを用意しつつコードをコピーしてデータを準備していただくと理解が進みます。
自分でやってみると気付く事や疑問に思う事が出てくることでしょう。
その疑問点を調べる事がクラスへの理解につながります。1回では難しいので何回かチャレンジしてみてください。