プログラミングにおいて、コードの設計は非常に重要です。
効率的で保守しやすいコードを書くために、多くのエンジニアが「デザインパターン」を利用しています。
デザインパターンとは、よくあるソフトウェア設計の問題に対する、再利用可能な解決策のことです。
本記事では、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()