VBSのクラスオブジェクト
VBScriptでちょっとだけオブジェクト指向に触れるという企画ものです。VBScriptのClassは機能がシンプルですが、うまく使えるようになるとそれなりに効果的です。せっかくですので、VBScriptでクラスを使ったスーパーテクニックなどを紹介しようと思います。
VBSのクラスオブジェクト利用メモの目次
クラス宣言
Classとはオブジェクトの設計図です。どんなデータと機能をまとめたオブジェクトにするか?をClassを使って定義します。
例えば「本」を管理するプログラムを書くとして、「タイトル」と「価格」のデータを扱いたいなら、以下のようにクラス定義します。
Option Explicit '「本」のクラス Class Book Public Title Public Price End Class
定義したクラスは、Newでオブジェクト(インスタンス)を取得して使います。
'「本」のオブジェクト(インスタンス)作成 Dim objBook Set objBook = New Book ' メンバへのアクセス objBook.Title = "VBSでちょっとオブジェクト第1巻" objBook.Price = 1500 '使い終わったオブジェクトは必ず廃棄 Set objBook = Nothing
メソッド
これだけだと、データを格納することしかできないので、「タイトル」と「価格」を整形して表示する機能をもたせることにします。先ほどのサンプルにShowという機能を加えたものが以下です。
Option Explicit '「本」のクラス Class Book Public Title Public Price Public Sub Show WScript.Echo "「" & Title & "」 " & Price & "円" End Sub End Class '「本」のオブジェクト(インスタンス)作成 Dim objBook Set objBook = New Book ' メンバへのアクセス objBook.Title = "VBSでちょっとオブジェクト第1巻" objBook.Price = 1500 '「本」を表示 objBook.Show '使い終わったオブジェクトは必ず廃棄 Set objBook = Nothing
プロパティ
Bookクラスを使っていたら以下のような不満がでてきました。
- Priceに数値以外のデータを入れることができる
- Titleに何文字でも入れることができる
これらは、データを入力する時点でチェックするようにします。そこで登場するのがプロパティです。いままで利用していたTitle,Priceという変数は、pTitle, pPrice に名前を変更し、Private宣言にします。こうすることによって、プロパティ経由でしか、変数に値を代入できなくすることができます。このようにクラス中のPrivateメンバにアクセスできないように保護する機能をカプセル化といいます。あなた以外の人がこのクラスを使って、開発を行っても安全にコーディングすることができます。:-)
'「本」のクラス Class Book Private pTitle Private pPrice Public Property Let Title(strTitle) If Len(strTitle) <= 20 Then pTitle = strTitle Else WScript.Echo "タイトルが長すぎます。" & vbCrLf & strTitle End If End Property Public Property Let Price(lngPrice) If IsNumeric(lngPrice) Then pPrice = lngPrice Else WScript.Echo "価格が数値ではありません。" & vbCrLf & lngPrice End If End Property Public Sub Show WScript.Echo "「" & pTitle & "」 " & pPrice & "円" End Sub End Class
コンストラクタ、デストラクタ
コンストラクタはオブジェクトの生成時に一度だけ呼び出されます。変数の初期化などを行ったりします。VBScript では、Sub Class_Initialize がコンストラクタとなります。またデストラクタは、オブジェクトの終了時に一度だけ呼び出されます。VBScript では、Sub Class_Terminate がデストラクタになります。 コンストラクタやデストラクタという呼び方よりも、そのままイニシャライズとターミネートと覚えた方がわかりやすくていいかもしれません。
'「本」のクラス Class Book Private pTitle Private pPrice Private Sub Class_Initialize pTitle = "" pPrice = 0 WScript.Echo "本を作成しました。" End Sub Private Sub Class_Terminate WScript.Echo "「" & pTitle & "」を廃棄しました。" End Sub Public Property Let Title(strTitle) If Len(strTitle) <= 20 Then pTitle = strTitle Else WScript.Echo "タイトルが長すぎます。" & vbCrLf & strTitle End If End Property Public Property Let Price(lngPrice) If IsNumeric(lngPrice) Then pPrice = lngPrice Else WScript.Echo "価格が数値ではありません。" & vbCrLf & lngPrice End If End Property Public Sub Show WScript.Echo "「" & pTitle & "」 " & pPrice & "円" End Sub End Class
クラス変数、クラスメソッド
クラス宣言では、インスタンスごとに利用できる変数やメソッドを定義しました。これらをインスタンス変数、インスタンスメソッドと呼びます。これに対して、インスタンスに関連付けられないメンバを持つことができます。これらをクラス変数、クラスメソッドと呼びます。例えば、現在ある本の冊数を知りたい場合などは、クラス変数を使います。
しかしながらVBScriptにはクラス変数、クラスメソッド用に特別な命令はありません。どうするかというと、グローバルな変数とメソッドを使います。本の冊数などは、クラス内のコンストラクタとデストラクタで、そのクラス変数をインクリメント、デクリメントしてあげます。また、クラス変数名とメソッド名は、クラス名を先頭に付けた方がよいでしょう。sumなどという単純な名前をつけてしまうと、複数のクラスを扱ってプログラムするときに、名前がバッティングしてしまうからです。
下のサンプルでは、Book_sum というようにクラス名を付けています。
Option Explicit ' クラス変数 Private Book_sum Book_sum = 0 ' クラスメソッド Public Sub Book_ShowSum WScript.Echo "本は" & Book_sum & "冊あります。" End Sub '「本」のクラス Class Book Private pTitle Private pPrice Private Sub Class_Initialize pTitle = "" pPrice = 0 WScript.Echo "本を作成しました。" Book_sum = Book_sum + 1 End Sub Private Sub Class_Terminate Book_sum = Book_sum - 1 WScript.Echo "「" & pTitle & "」を廃棄しました。" End Sub Public Property Let Title(strTitle) If Len(strTitle) <= 20 Then pTitle = strTitle Else WScript.Echo "タイトルが長すぎます。" & vbCrLf & strTitle End If End Property Public Property Let Price(lngPrice) If IsNumeric(lngPrice) Then pPrice = lngPrice Else WScript.Echo "価格が数値ではありません。" & vbCrLf & lngPrice End If End Property Public Sub Show WScript.Echo "「" & pTitle & "」 " & pPrice & "円" End Sub End Class Book_ShowSum '「本」のオブジェクト(インスタンス)作成 Dim objBook1, objBook2 Set objBook1 = New Book objBook1.Title = "VBSでちょっとオブジェクト上巻" objBook1.Price = 1500 objBook1.Show Book_ShowSum Set objBook2 = New Book objBook2.Title = "VBSでちょっとオブジェクト下巻" objBook2.Price = 1500 objBook2.Show Book_ShowSum Set objBook1 = Nothing Book_ShowSum Set objBook2 = Nothing Book_ShowSum
作成したクラスをパッケージ化する
別に特別なことをするわけではなく、クラス定義の部分を別ファイルにしておきます。こうすることで、いつでもそのクラスを読み込んで、利用することができるようになります。上のサンプルからクラス部分を抜き出し book.class という名前で保存し、利用するときは、wsfなら以下のようにbook.classを読み込んで使います。
<job> <script language="vbscript" src="book.class"/> <script language="vbscript"> '---------------------------------------------- Dim objBook Set objBook = New Book objBook.Title = "VBSでちょっとオブジェクト上巻" objBook.Price = 1500 objBook.Show Set objBook = Nothing '---------------------------------------------- </script> </job>
オブジェクトの配列
オブジェクトの配列を使えるようになると、いっぱしの事ができるようになります。サンプルは上で作成した「本」クラスからちょっと変更してあります。プロパティをSubにして一度に設定できるようにしてあります。
book.class
Option Explicit Private Book_sum Book_sum = 0 Public Sub Book_ShowSum WScript.Echo "本は" & Book_sum & "冊あります。" End Sub '「本」のクラス Class Book Private pTitle Private pPrice Private Sub Class_Initialize pTitle = "" pPrice = 0 WScript.Echo "本を作成しました。" Book_sum = Book_sum + 1 End Sub Private Sub Class_Terminate Book_sum = Book_sum - 1 WScript.Echo "「" & pTitle & "」を廃棄しました。" End Sub Public Sub SetBook(strTitle, lngPrice) If Len(strTitle) <= 20 Then pTitle = strTitle Else WScript.Echo "タイトルが長すぎます。" & vbCrLf & strTitle End If If IsNumeric(lngPrice) Then pPrice = lngPrice Else WScript.Echo "価格が数値ではありません。" & vbCrLf & lngPrice End If End Sub Public Sub Show WScript.Echo "「" & pTitle & "」 " & pPrice & "円" End Sub End Class
book.wsf
<job> <script language="vbscript" src="book.class"/> <script language="vbscript"> Dim objBooks(3), objBook, n For n = 0 To 3 Set objBooks(n) = New Book Next objBooks(0).SetBook "hogehoge大全集", 4200 objBooks(1).SetBook "foofoo大百科", 14800 objBooks(2).SetBook "barbarの歌", 1200 objBooks(3).SetBook "punipuni辞典", 3500 Book_ShowSum For Each objBook In objBooks objBook.Show Set objBook = Nothing Next </script> </job>
オブジェクト配列を使ったテクニック
上記例では、book.class のみを配列にして使っていますが、異なるクラスに同名のメソッドを用意しておくと、配列でオブジェクトごとに違った処理を一気に行うことができます。下記は「本」クラスと同等のメンバを持つ、「絵本」クラス(PictureBook.class)を配列を使って一気に処理するサンプルです。「絵本」クラスは、対象年齢という新たなメンバを持っています。
PictureBook.class
Option Explicit Private PictureBook_sum PictureBook_sum = 0 Public Sub PictureBook_ShowSum WScript.Echo "絵本は" & PictureBook_sum & "冊あります。" End Sub '「絵本」のクラス Class PictureBook Private pTitle Private pPrice Private pTarget Private Sub Class_Initialize pTitle = "" pPrice = 0 pTarget = "" WScript.Echo "絵本を作成しました。" PictureBook_sum = PictureBook_sum + 1 End Sub Private Sub Class_Terminate PictureBook_sum = PictureBook_sum - 1 WScript.Echo "「" & pTitle & "」を廃棄しました。" End Sub Public Sub SetBook(strTitle, lngPrice, strTarget) If Len(strTitle) <= 20 Then pTitle = strTitle Else WScript.Echo "タイトルが長すぎます。" & vbCrLf & strTitle End If If IsNumeric(lngPrice) Then pPrice = lngPrice Else WScript.Echo "価格が数値ではありません。" & vbCrLf & lngPrice End If pTarget = strTarget End Sub Public Sub Show WScript.Echo "「" & pTitle & "」 " & pPrice & "円" & vbCrLf & _ "対象年齢:" & pTarget End Sub End Class
book.wsf
<job> <script language="vbscript" src="Book.class"/> <script language="vbscript" src="PictureBook.class"/> <script language="vbscript"> Dim objBooks(5), objBook, n Set objBooks(0) = New Book Set objBooks(1) = New Book Set objBooks(2) = New Book Set objBooks(3) = New Book Set objBooks(4) = New PictureBook Set objBooks(5) = New PictureBook objBooks(0).SetBook "hogehoge大全集", 4200 objBooks(1).SetBook "foofoo大百科", 14800 objBooks(2).SetBook "barbarの歌", 1200 objBooks(3).SetBook "punipuni辞典", 3500 objBooks(4).SetBook "さるエビ合戦", 1200, "3歳〜5歳" objBooks(5).SetBook "ヨンデレラ", 900, "3歳〜5歳" Book_ShowSum PictureBook_ShowSum For Each objBook In objBooks objBook.Show Set objBook = Nothing Next </script> </job>
継承
オブジェクト指向プログラミング(OOP)では、上記の本、絵本クラスのような関係は継承という手法を使うのが普通です。継承を使うと、クラスで親子関係を作ることができ、クラスの再利用価値も高まります。
VBSには継承機能はありませんので、もしVBSでクラスの継承をするなら、擬似的にこれを実現できるような形にクラスを設計する必要があります。
- コンストラクタでスーパークラス(親)のインスタンスを取得
- デストラクタでインスタンスを廃棄する
- 継承したいフィールド(変数)は、スーパークラスのインスタンス経由でアクセスする
- 継承させるフィールドは、読み込み用のプロパティ(Get)を用意する
今回用意したサンプルは結構長いです。3つのクラスを使います。「本」->「まんが本」->「古まんが本」というように継承していくので、擬似的な継承のしくみをある程度理解できると思います。
ダウンロード: book.lzh
Book.class
Option Explicit ' Book.class - 「本」のクラス Private Book_sum Book_sum = 0 Public Sub Book_ShowSum WScript.Echo "本は" & Book_sum & "冊あります。" End Sub Class Book Private pTitle Private pPrice Private Sub Class_Initialize pTitle = "" pPrice = 0 Book_sum = Book_sum + 1 End Sub Private Sub Class_Terminate Book_sum = Book_sum - 1 End Sub Public Property Get Title Title = pTitle End Property Public Property Get Price Price = pPrice End Property Public Sub SetBook(strTitle, lngPrice) If Len(strTitle) <= 20 Then pTitle = strTitle Else WScript.Echo "タイトルが長すぎます。" & vbCrLf & strTitle End If If IsNumeric(lngPrice) Then pPrice = lngPrice Else WScript.Echo "価格が数値ではありません。" & vbCrLf & lngPrice End If End Sub Public Sub Show WScript.Echo "「" & pTitle & "」 " & pPrice & "円" End Sub End Class
ComicBook.class
Option Explicit ' ComicBook.class - 「まんが本」のクラス Private ComicBook_sum ComicBook_sum = 0 Public Sub ComicBook_ShowSum WScript.Echo "マンガ本は" & ComicBook_sum & "冊あります。" End Sub Class ComicBook Private pTarget Private objSuper Private Sub Class_Initialize Set objSuper = New Book pTarget = "" ComicBook_sum = ComicBook_sum + 1 End Sub Private Sub Class_Terminate ComicBook_sum = ComicBook_sum - 1 Set objSuper = Nothing End Sub Public Property Get Title Title = objSuper.Title End Property Public Property Get Price Price = objSuper.Price End Property Public Property Get Target Target = pTarget End Property Public Sub SetBook(strTitle, lngPrice, strTarget) objSuper.SetBook strTitle, lngPrice pTarget = strTarget End Sub Public Sub Show WScript.Echo "「" & objSuper.Title & "」 " & _ objSuper.Price & "円" & vbCrLf & _ "対象年齢:" & pTarget End Sub End Class
UseComicBook.class
Option Explicit ' UsedComicBook.class - 「古まんが本」のクラス Private UsedComicBook_sum UsedComicBook_sum = 0 Public Sub UsedComicBook_ShowSum WScript.Echo "古マンガ本は" & UsedComicBook_sum & "冊あります。" End Sub Class UsedComicBook Private pPercentDiscount Private objSuper Private Sub Class_Initialize Set objSuper = New ComicBook pPercentDiscount = 20 UsedComicBook_sum = UsedComicBook_sum + 1 End Sub Private Sub Class_Terminate UsedComicBook_sum = UsedComicBook_sum - 1 Set objSuper = Nothing End Sub Public Property Get Title Title = objSuper.Title End Property Public Property Get Price Price = objSuper.Price End Property Public Property Get Target Target = objSuper.Target End Property Public Property Get PercentDiscount PercentDiscount = pPercentDiscount End Property Public Property Get DiscountPrice DiscountPrice = CInt(objSuper.Price - objSuper.Price * (pPercentDiscount / 100)) End Property Public Sub SetBook(strTitle, lngPrice, strTarget, sngPercentDiscount) objSuper.SetBook strTitle, lngPrice, strTarget If IsNumeric(sngPercentDiscount) And _ sngPercentDiscount > 0 And sngPercentDiscount < 100 Then pPercentDiscount = sngPercentDiscount Else MsgBox "値引率が異常です。" End If End Sub Public Sub Show WScript.Echo "「" & objSuper.Title & "」 " & _ objSuper.Price & "円 → " & DiscountPrice & "円" & vbCrLf & _ "対象年齢:" & objSuper.Target End Sub End Class
book.wsf
<job> <script language="vbscript" src="Book.class"/> <script language="vbscript" src="ComicBook.class"/> <script language="vbscript" src="UsedComicBook.class"/> <script language="vbscript"> Dim objBooks(4), objBook, n Set objBooks(0) = New Book Set objBooks(1) = New Book Set objBooks(2) = New ComicBook Set objBooks(3) = New UsedComicBook Set objBooks(4) = New ComicBook objBooks(0).SetBook "hogehoge大全集", 4200 objBooks(1).SetBook "foofoo大百科", 14800 objBooks(2).SetBook "竜玉", 500, "10歳〜" objBooks(3).SetBook "竜玉", 500, "10歳〜", 40.0 objBooks(4).SetBook "変人伝", 1200, "成人向" Book_ShowSum ComicBook_ShowSum UsedComicBook_ShowSum For Each objBook In objBooks objBook.Show Set objBook = Nothing Next </script> </job>