FrontPage 新規 編集 検索 一覧 ヘルプ

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クラスを使っていたら以下のような不満がでてきました。

これらは、データを入力する時点でチェックするようにします。そこで登場するのがプロパティです。いままで利用していた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でクラスの継承をするなら、擬似的にこれを実現できるような形にクラスを設計する必要があります。

今回用意したサンプルは結構長いです。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>
Yesterday Today Total