小さなクラスを作る(4)~フォルダ作成クラス

きっかけ

写しハンコつきPDFを作るマクロのコードを見直していると、メインの「写しPDFを作るクラス」が結構複雑なクラスになっていることが分かった。ざっと挙げると、

  • 元のWordドキュメントに画像があるかどうかチェック
  • ハンコ用の画像ファイルが実在するかチェック
  • 各ページの先頭中央にハンコ画像を追加する
  • 保存用のフォルダの有無をチェックし、なければ作る
  • ハンコ画像付きのWordドキュメントをPDFに変換して保存
  • Wordドキュメントを閉じてWordを終了

とまあ、これだけのことを一つのクラスの一つのメソッドに請け負わせていたことになる。

こうやって改めて書き出してみたら、イマイチだなあ……、とw

たとえば、4つ目の保存先のフォルダの有無をチェックし、なければ作るなんてのは、他のマクロでも頻出の処理だし、5つ目のWordドキュメントをPDF化して保存なんてのも他で使い回せそうだ。

ということは、切り出して独立したクラスにしといた方が良いということだろう。

だから、やってみた。

フォルダの有無をチェックしてなければ作るクラス

例によってクラスモジュールを挿入。オブジェクト名は「FolderCreator」とした。

フィールド部分

Private objFolder_ As myFolder

ちょっと実験的に、フィールド部分を構造体にしてみた。

ただ、構造体の定義をクラスモジュールに書けたら分かりやすいんだけど、それはさせてもらえず、標準モジュールにPublicで書かざるを得なかった。

標準モジュールの宣言セクション

Public Type myFolder
  fdPath As String
  fdName As String
  fdIsExist As Boolean
  fdIsCreated As Boolean
End Type
Public Const SAVE_FOLDER As String = "写しPDF"
Public fc As FolderCreator

これがうまいやり方なのかどうかは分からないけど、

対象のフォルダが存在する

ということを表すんだったら、たとえば

fc.isExist = True


と書くよりも、

fc.objFolder.fdIsExist = True


と書く方が分かりやすいと思ったのだ。

ただ、構造体を定義する場所と実際に使う場所が離れてしまうので、保守性という点ではイマイチなのかも知れない。

アクセサ部分

'アクセサ
Public Property Get objFolder() As myFolder
  objFolder = objFolder_
End Property

構造体としてまとめた関係で、このように異様にシンプルになる。

メソッド部分

2つのメソッドを作ってみた。

'メソッド
Public Sub checkExistenceOfFolder(ByVal tgtPath As String, _
                                  ByVal folderName As String)  '……(1)
  With objFolder_
    .fdIsExist = False
    .fdPath = tgtPath
    .fdName = folderName
    If Dir(.fdPath & "\" & .fdName, vbDirectory) = "" Then     '……(2)
      .fdIsExist = False
    Else
      .fdIsExist = True
    End If
  End With
End Sub

Public Sub createFolder(ByVal tgtPath As String, _
                        ByVal folderName As String)            '……(3)
  With objFolder_
    .fdIsExist = False
    .fdIsCreated = False
    .fdPath = tgtPath
    .fdName = folderName
    If Dir(.fdPath & "\" & .fdName, vbDirectory) = "" Then
      MkDir .fdPath & "\" & .fdName                            '……(4)
      .fdIsCreated = True                                      '……(5)
      .fdIsExist = True
    Else
      .fdIsExist = True
    End If
  End With
End Sub

1つ目のcheckExistenceOfFolderメソッド(1)は、その名の通り、指定したフォルダが存在するかどうかを調べるメソッド。存在していたらfdIsExistプロパティにTrueが格納される。それだけ。使い道があるのかどうかは不明w

2つ目のcreteFolderメソッド(3)は、第1引数で指定したフォルダに、第2引数で指定した名前のフォルダがあるかどうかを調べて、なかったら作る、というもの。

コードの説明

  • (1)からのcheckExistenceOfFolderメソッド。(2)では、Dir関数を使ってフォルダが存在するかどうか調べている。引数で指定したフォルダが存在しなかったら、Dir関数は""を返す。これはよく使うと思う。
  • (3)からのcreteFolderメソッド。checkExistenceOfFolderメソッドと同じようにフォルダの有無を調べて、なかったら(4)でMkDirステートメントで新しいフォルダを作成している。
  • 新たにフォルダを作成した場合は、元々フォルダが存在していた場合と区別するために、(5)でobjFolderプロパティのfdIsCreated要素(擬似的なプロパティ)をTrueにする。

おわりに

構造体の使用の是非はともかく、

他で使い回せるかどうか

を基準に、クラスを切り分けていくのが良いのかもしれない。

@akashi_keirin on Twitter

平成29年3月20日追記

なずな (id:nazuna_0124)さんからのコメントを見て気がついた。

エラー対応がないんじゃね???

ということに。

だからといって、今さら上の方を書き改めるのはメンドクサイので、とりあえず現段階での「FolderCreator」クラスのコードだけ投げやりに載っけとこう。

クラスFolderCreatorのコード

Option Explicit
'フィールド
Private objFolder_ As myFolder
Private hasError_ As Boolean
'アクセサ
Public Property Get objFolder() As myFolder
  objFolder = objFolder_
End Property
Public Property Get hasError() As Boolean
  hasError = hasError_
End Property
'コンストラクタ

'メソッド
Public Sub checkExistenceOfFolder(ByVal tgtPath As String, _
                                  ByVal folderName As String)
  With objFolder_
    .fdIsExist = False
    .fdPath = tgtPath
    .fdName = folderName
    If Dir(.fdPath & "\" & .fdName, vbDirectory) = "" Then
      .fdIsExist = False
    Else
      .fdIsExist = True
    End If
  End With
End Sub

Public Sub createFolder(ByVal tgtPath As String, _
                        ByVal folderName As String)
On Error Resume Next
  Err.Clear
  hasError_ = False
  With objFolder_
    .fdIsExist = False
    .fdIsCreated = False
    .fdPath = tgtPath
    .fdName = folderName
    If Dir(.fdPath & "\" & .fdName, vbDirectory) = "" Then
      MkDir .fdPath & "\" & .fdName
      .fdIsCreated = True
      .fdIsExist = True
    Else
      .fdIsExist = True
    End If
  End With
  If Err.Number > 0 Then
    hasError_ = True
  End If
End Sub

いちおう説明

「hasError」というプロパティを加えた。エラーが発生していたら、Trueになるので、メソッドの呼び出し元で条件判定してエラー時の処理を書けばよい。エラー時の処理にはたぶんErr.Numberとか、Err.Descriptionを使うことになるだろうから、「On Error GoTo 0」は書いていない。