VBScriptで学ぶデザインパターン入門:効率的なコード設計法

プログラミングにおいて、コードの設計は非常に重要です。

効率的で保守しやすいコードを書くために、多くのエンジニアが「デザインパターン」を利用しています。

デザインパターンとは、よくあるソフトウェア設計の問題に対する、再利用可能な解決策のことです。

本記事では、VBScriptという古典的なスクリプト言語を使いながら、デザインパターンの基本的な考え方を学び、実際に適用する方法を解説します。

デザインパターンとは?

デザインパターンとは、特定の課題に対して最適な解決策を提供するテンプレートです。

これを用いることで、開発者は以下の利点を得ることができます:

  • コードの再利用性の向上
  • 保守性の向上
  • 開発時間の短縮

デザインパターンは大きく3つに分類されます:

  • 生成パターン:オブジェクトの生成に関するパターン(例:シングルトン、ファクトリーパターン)
  • 構造パターン:オブジェクトの構造を整理するためのパターン(例:デコレータ、アダプタパターン)
  • 振る舞いパターン:オブジェクト間の連携や責任の分担を整理するパターン(例:オブザーバー、コマンドパターン)

VBScriptとデザインパターンの相性

VBScriptは、特にウェブブラウザや簡単な自動化スクリプトで利用される軽量スクリプト言語ですが、オブジェクト指向の考え方をサポートしており、デザインパターンを実装することも可能です。

VBScriptは、クラスの概念こそ持たないものの、関数プロシージャオブジェクトの仕組みを利用することで多くのデザインパターンを適用できます。


VBScriptでのデザインパターンの実装方法

シングルトンパターン

シングルトンパターンは、クラスのインスタンスが常に一つだけであることを保証し、グローバルなアクセスを可能にする設計パターンです。

VBScriptでは、静的なクラス変数が存在しないため、グローバル変数を活用する手法が一般的です。

実装例
' グローバル変数としてシングルトンのインスタンスを定義
Dim objSingleton
If IsEmpty(objSingleton) Then
    Set objSingleton = New Singleton
End If

Class Singleton
    Private data

    Private Sub Class_Initialize()
        data = "初期データ"
    End Sub

    Public Function GetData()
        GetData = data
    End Function

    Public Sub SetData(newData)
        data = newData
    End Sub
End Class

' 利用例
WScript.Echo "シングルトンデータ: " & objSingleton.GetData()
objSingleton.SetData "更新後のデータ"
WScript.Echo "更新されたデータ: " & objSingleton.GetData()

この例では、グローバル変数 objSingleton を利用してインスタンスの一意性を確保しています。VBScriptならではのシンプルな実装方法ですが、同時にスコープ管理や再初期化に注意が必要です。

ファクトリーパターン

ファクトリーパターンは、オブジェクトの生成過程を抽象化し、クライアントコードから生成の詳細を隠蔽するための設計手法です。VBScriptにおいても、生成処理を独立したサブルーチンや関数に委譲することで、同様のメリットを享受できます。

実装例
' オブジェクト生成を行うファクトリ関数
Function CreateLogger(type)
    Select Case LCase(type)
        Case "console"
            Set CreateLogger = New ConsoleLogger
        Case "file"
            Set CreateLogger = New FileLogger
        Case Else
            Set CreateLogger = Nothing
    End Select
End Function

' コンソール出力用ロガークラス
Class ConsoleLogger
    Public Sub Log(message)
        WScript.Echo "Console Log: " & message
    End Sub
End Class

' ファイル出力用ロガークラス(簡易例)
Class FileLogger
    Private filePath
    Private fileSystem

    Private Sub Class_Initialize()
        filePath = "C:\temp\log.txt"
        Set fileSystem = CreateObject("Scripting.FileSystemObject")
    End Sub

    Public Sub Log(message)
        Dim file
        Set file = fileSystem.OpenTextFile(filePath, 8, True)
        file.WriteLine Now() & " - " & message
        file.Close
    End Sub
End Class

' 利用例
Dim logger
Set logger = CreateLogger("console")
If Not logger Is Nothing Then
    logger.Log "ファクトリーパターンの実装例です。"
End If

このように、ファクトリーパターンを用いることで、生成するオブジェクトの種類を動的に選択し、クライアントコードの柔軟性を向上させることが可能です。

オブザーバーパターン

オブザーバーパターンは、あるオブジェクト(被観測者)の状態変化を複数のオブジェクト(オブザーバー)に通知する仕組みです。

VBScriptではイベントドリブンな設計が難しいものの、擬似的な実装方法として、通知リストを管理する方法が考えられます。

実装例
' オブザーバー管理クラス
Class Subject
    Private observers
    Private state

    Private Sub Class_Initialize()
        Set observers = CreateObject("Scripting.Dictionary")
        state = ""
    End Sub

    Public Sub Attach(key, observer)
        observers.Add key, observer
    End Sub

    Public Sub Detach(key)
        If observers.Exists(key) Then
            observers.Remove key
        End If
    End Sub

    Public Sub SetState(newState)
        state = newState
        NotifyAll
    End Sub

    Public Function GetState()
        GetState = state
    End Function

    Private Sub NotifyAll()
        Dim key
        For Each key In observers.Keys
            observers.Item(key).Update state
        Next
    End Sub
End Class

' オブザーバークラスの例
Class Observer
    Public Sub Update(newState)
        WScript.Echo "オブザーバーに通知: 状態が " & newState & " に変化しました。"
    End Sub
End Class

' 利用例
Dim subject, observer1, observer2
Set subject = New Subject
Set observer1 = New Observer
Set observer2 = New Observer

subject.Attach "obs1", observer1
subject.Attach "obs2", observer2

subject.SetState "Active"

この実装では、Scripting.Dictionary を用いて複数のオブザーバーを管理し、被観測者の状態変化時に全オブザーバーへ通知しています。VBScriptの制約の中で、オブザーバーパターンの基本概念を理解する一助となります。

デコレーターパターン

デコレーターパターンでは、オブジェクトに動的に機能を追加することができます。

基本的なクラスに対して、追加の責務を与えるために使用されます。

Class Component
    Public Sub Operation()
        MsgBox "Basic Operation"
    End Sub
End Class

Class Decorator
    Private component
    
    Public Sub SetComponent(c)
        Set component = c
    End Sub
    
    Public Sub Operation()
        component.Operation
        MsgBox "Added Operation by Decorator"
    End Sub
End Class

' デコレーターパターンの使用例
Dim component, decorator
Set component = New Component
Set decorator = New Decorator

decorator.SetComponent component
decorator.Operation()

DecoratorクラスがComponentクラスをラップし、元の機能に加えて追加の動作を実行します。

ストラテジーパターンとその他のパターン

ストラテジーパターンは、アルゴリズムや処理の切り替えを柔軟に行うための手法です。

VBScriptでは、関数ポインタのような機能は直接はありませんが、クラスを用いたインターフェース風の実装が可能です。

たとえば、処理の共通インターフェースを持つ複数のクラスを用意し、実行時に適切なクラスのインスタンスを選択することで、ストラテジーパターンの考え方を実現できます。

実装例(簡易版):ストラテジーを利用した計算処理
' インターフェース風計算クラス
Class CalculatorStrategy
    Public Function Calculate(a, b)
        ' 仮の実装(オーバーライドを前提)
        Calculate = 0
    End Function
End Class

' 加算戦略クラス
Class AdditionStrategy
    Public Function Calculate(a, b)
        Calculate = a + b
    End Function
End Class

' 減算戦略クラス
Class SubtractionStrategy
    Public Function Calculate(a, b)
        Calculate = a - b
    End Function
End Class

' コンテキストクラス
Class CalculatorContext
    Private strategy

    Public Sub SetStrategy(obj)
        Set strategy = obj
    End Sub

    Public Function ExecuteStrategy(a, b)
        If Not strategy Is Nothing Then
            ExecuteStrategy = strategy.Calculate(a, b)
        Else
            ExecuteStrategy = "戦略が設定されていません"
        End If
    End Function
End Class

' 利用例
Dim calc, addStrategy, subStrategy
Set calc = New CalculatorContext
Set addStrategy = New AdditionStrategy
Set subStrategy = New SubtractionStrategy

calc.SetStrategy addStrategy
WScript.Echo "加算結果: " & calc.ExecuteStrategy(10, 5)

calc.SetStrategy subStrategy
WScript.Echo "減算結果: " & calc.ExecuteStrategy(10, 5)

このように、異なるアルゴリズムを実行時に切り替えることができるため、将来的な機能拡張やメンテナンス性の向上に寄与します。

その他、DecoratorパターンやFacadeパターンなど、必要に応じたパターンの応用も可能ですが、VBScriptのシンプルな構造ゆえに、基本概念の理解が優先されるべきでしょう。


デザインパターンの適用例

これまでに紹介したパターンは、さまざまな場面で利用できます。

シングルトンパターンは設定情報の管理、ファクトリーパターンはオブジェクト生成の集中管理に役立ちます。

ストラテジーパターンは異なるアルゴリズムを容易に切り替えたい場合、オブザーバーパターンはイベント通知システムの設計に、デコレーターパターンは既存のオブジェクトに機能を追加する場合に使えます。


まとめ

この記事では、VBScriptでのデザインパターンの実装を紹介しました。

シングルトン、ファクトリー、ストラテジー、オブザーバー、デコレーターパターンといった代表的なデザインパターンを使うことで、再利用性の高い保守性のあるコードが書けるようになります。

VBScriptはオブジェクト指向プログラミングの機能が限定的ですが、デザインパターンを適用することで効率的な設計が可能です。


演習問題と解答例

この記事の内容を基にした演習問題を通じて、実際に手を動かしながら学習を進めましょう。

演習問題1: オブザーバーパターンの実装

NewsPublisherクラスを作成し、複数のSubscriberクラスがそのニュースを購読して通知を受け取る仕組みを実装してください。

ニュースが更新された際に、全ての購読者に通知が送られるようにしてください。

演習1:解答例
Class NewsPublisher
    Private subscribers
    
    Private Sub Class_Initialize()
        Set subscribers = CreateObject("Scripting.Dictionary")
    End Sub
    
    Public Sub AddSubscriber(s)
        subscribers.Add subscribers.Count + 1, s
    End Sub
    
    Public Sub NotifySubscribers(news)
        Dim s
        For Each s In subscribers
            s.Update(news)
        Next
    End Sub
End Class

Class Subscriber
    Public Sub Update(news)
        MsgBox "Received News: " & news
    End Sub
End Class

' 使用例
Dim publisher, subscriber1, subscriber2
Set publisher = New NewsPublisher
Set subscriber1 = New Subscriber
Set subscriber2 = New Subscriber

publisher.AddSubscriber subscriber1
publisher.AddSubscriber subscriber2

' ニュースを通知
publisher.NotifySubscribers "New Article Published!"

演習問題2: デコレーターパターンの実装

Carクラスに、Driveメソッドを持たせ、デコレーターでSportsCarやLuxuryCarとして追加機能(例えば、スピードの表示や快適なドライブメッセージ)を付け加える実装をしてください。

演習2:解答例
Class Car
    Public Sub Drive()
        MsgBox "Driving a car."
    End Sub
End Class

Class SportsCarDecorator
    Private car
    
    Public Sub SetCar(c)
        Set car = c
    End Sub
    
    Public Sub Drive()
        car.Drive
        MsgBox "Driving fast with a sports car!"
    End Sub
End Class

Class LuxuryCarDecorator
    Private car
    
    Public Sub SetCar(c)
        Set car = c
    End Sub
    
    Public Sub Drive()
        car.Drive
        MsgBox "Driving in luxury!"
    End Sub
End Class

' 使用例
Dim car, sportsCar, luxuryCar
Set car = New Car
Set sportsCar = New SportsCarDecorator
Set luxuryCar = New LuxuryCarDecorator

sportsCar.SetCar car
luxuryCar.SetCar car

' スポーツカーとして運転
sportsCar.Drive()

' ラグジュアリーカーとして運転
luxuryCar.Drive()