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>
{{edit 更新履歴}}