サイトアイコン わんすけに聞いてみる

vbscriptでクラスを考えてみる。①

スクリプトから勉強し始めの方に、クラスってなんで必要なのか?
クラスってどういう考え方なのか?を理解できるように考えてみる。

例えば - vbscriptの場合、こんな処理を考えてみる。

2つのテキストファイルを読み込む処理

Dim fso, f, log1, log2
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile("yyyymmdd.log", 1)
log1 = f.ReadAll
f.Close

Set f = fso.OpenTextFile("err_msg.log", 1)
log2 = f.ReadAll
f.Close
Set fso = Nothing

スクリプトを勉強し始めると、最初の理解ではこの様なコードを考える。
で、このコードを実行してテキストの読み込み処理をしていると
そのうち、「あっ、ファイルがなかったからエラーになった」という場面にぶつかる。
なので、ファイルを読み込む処理の前にファイルの存在確認をしようってなる。
ファイルの存在確認の際にも「FileSystemObject」のインスタンスを使うことになる。

' ファイルの存在確認
If fso.FileExists("yyyymmdd.log") Then
  ' あれば、ファイルを読み込む
  Set f = fso.OpenTextFile("yyyymmdd.log", 1)
  log1 = f.ReadAll
  f.Close
End If

みたいな感じ。

こうやって、fsoを使いまわしていくと
どこでfsoを破棄(Set fso = Nothing)すればいいのかわからなくなってきちゃいます。
・・・そして、Nothingし忘れる。あるいは、Nothingしたあとでfsoを使おうとしてエラーになる。
なので、まずは Sub や Function を使って処理のルーチン化・ファンクション化することを考えるようになる。

とりあえず、機能を纏めてみる。
' ファイルの存在を確認して True or Falseを返す。
Function FileExists(path)
  Dim fso
  Set fso = CreateObject("Scripting.FileSystemObject")
  FileExists = fso.FileExists(path)
  Set fso = Nothing
End Function

' テキストファイルを読み込んで中身を戻り値として返す。
Function ReadText(path)
  Dim fso, f, rdtxt
  Set fso = CreateObject("Scripting.FileSystemObject")
  Set f = fso.OpenTextFile(path, 1)
  rdtxt = f.ReadAll
  f.Close
  Set fso = Nothing
  ReadText = rstxt
End Function

単純なファイル操作のスクリプトを書く分には、これでも全然大丈夫。
でも、実業務では、ここからさらにテキストへの書きこみ(上書き/追記)だったり
フォルダの作成・ファイルの削除など、「FileSystemObject」を使いまわす処理がたくさん出てきます。
すると、いろんな Sub や Function の中でいたるところでfsoの生成・破棄を繰り返す様になってしまいます。
そのくらいまで、いろいろやらなきゃいけないことが増えてきたときに便利な考え方がクラスになります。

Class ~ End Class の間では、Private Publicといった
クラスの中で宣言した関数やルーチン、ファンクションをどこまで公開するか?
という概念が備わっていて、クラスの中で使い回したりクラスを破棄することでクラスの中で使っていたリソースを一気に破棄することが簡単にできるようになっちゃうのです。

 vbscript Class の例
Class clsText
  ' fsoはクラスのインスタンスの中で共有的に使える。
  ' Private で宣言されたコードはインスタンスの外から名前呼んでも参照できない。
  Private fso
  Private Sub Class_Initialize
    ' New された時に実行される処理
    Set fso = CreateObject("Scripting.FileSystemObject")
  End Sub

  Private Sub Class_Terminate
    ' Nothing とかでインスタンスが破棄された時に実行される処理
    Set fso = Nothing
  End Sub

  ' Publicで宣言されたコードはインスタンスの外から呼び出しができる。
  Public Function ReadText(path)
    Dim f
    If fso.FileExists(Path) Then
      Set f = fso.OpenTextFile(Path, 1)
      ReadText = f.ReadAll
      f.Close
    Else
      ReadText = ""
    End If
  End Function
End Class

クラスclsTextのインスタンスからファイル操作を行うことで
「Set fso = CreateObject("Scripting.FileSystemObject")」の記述量をうんと削減できます。
テキストを読み込む場合

Dim clsTxt
' インスタンスを生成してclsTxtに入れる。
Set clsTxt = New clsText
log1 = clsTxt.ReadText("yyyymmdd.log") ' Public Functionをインスタンスから呼び出す。
log2 = clsTxt.ReadText("err_msg.log")
Set clsTxt = Nothing ' インスタンスは忘れずに破棄しようね。

①.New clsText → clsTextのインスタンスを生成してます。
→ インスタンスが生成されると、「Private Sub Class_Initialize」が呼び出されてFileSystemObjectのインスタンスがfsoに入ります。
fsoは、Privateで宣言されているので、Classの外からは参照できず、
クラスの中のSub や Functionからのみ共有で使うことができます。

②.clsTxt.ReadText → clsTextのReadTextを実行してテキストの読み込みを行います。
①でfsoにFileSystemObjectを生成済みなので、余計な宣言がいらなくなってますね!
→ 何回でも呼び出して大丈夫!

③.Set clsTxt = Nothing → インスタンスを破棄してます。
→ インスタンスが破棄されると、「Private Sub Class_Terminate」が呼び出されて
fsoの破棄が行われます。
これで、Set fso = Nothing のタイミングを考えなくてもよくなりますね♪

こうやって、必要な処理を集約して、記述を纏めて、ブロック化することで
違う処理でまたスクリプトが必要になった時には、この Class ~ End Classをコピペで持って行って必要なファンクションだけ呼び出して使えば楽ちんになりますね♪

こうゆうのを、難しい参考書では「クラス化によってコードの再利用性を高める」なんて表現しています。
インスタンスごとに構造体としてデータを管理することで受けられる恩恵とか、他にもいいことはいろいろあるんですが、今日のところはこんなところで、またの機会にしましょう。

モバイルバージョンを終了