←
▼
▲
定数は、静的オブジェクトのメンバー変数に格納します。
定数シンボルが、グローバル・スコープのシンボルではなくなるので、名前の衝突の可能性が
低くなります。
Class SampleClass
Public c
Private Sub Class_Initialize()
Set Me.c = get_SampleConst()
End Sub
End Class
'***********************************************************************
'* Function: get_SampleConst
'***********************************************************************
Dim g_SampleConst
Function get_SampleConst()
If IsEmpty( g_SampleConst ) Then _
Set g_SampleConst = new SampleConstClass
Set get_SampleConst = g_SampleConst
End Function
Class SampleConstClass
Public CutTag, ExistOnly
Private Sub Class_Initialize()
CutTag = 1
ExistOnly = 2
End Sub
Public Function ToStr( Number )
Select Case Number
Case CutTag : ToStr = "CutTag"
Case ExistOnly : ToStr = "ExistOnly"
End Select
End Function
End Class
使用例1:
Set c = get_SampleConstClass()
copy_ex src, dst, c.CutTag
c.CutTag
get_SampleConstClass
CutTag
CutTag
使用例2:
Sub main()
Set object = new ClassA
copy_ex source, destination, object.c.CutTag
End Sub
object.c.CutTag
get_SampleConst
参考
'***********************************************************************
'* Function: get_SampleConst
'***********************************************************************
Dim g_SampleConst
Function get_SampleConst()
If IsEmpty( g_SampleConst ) Then _
Set g_SampleConst = new SampleConstClass
Set get_SampleConst = g_SampleConst
End Function
Class SampleConstClass
Public CutTag, ExistOnly
Private Sub Class_Initialize()
CutTag = 1
ExistOnly = 2
End Sub
End Class
get_SampleConst
キーワード:
グローバル変数
メンバー変数、文字列変換
ToStr
CutTag
CutTag
←
▼
▲
多くの場合、クラスに所属しない関数の中の最初で get_Object した後で、Validate メソッド
を呼び出します。 Validate は、パラメータに指定した状態にオブジェクトをするメソッドです。
Sub FuncA()
Dim m : Set m = get_ClassA() : m.Validate Empty
m.MethodA
End Sub
演算器的なオブジェクトとは、インスタンスを特定する必要が無いオブジェクトのことで、
切り替えることはあっても同時に複数存在しなくても目的の出力が得られるものです。
ユーザーは演算器の種類 (ClassV) を指定します。
ClassV_validate で取得したオブジェクトを明示的に削除する必要はありません。
明示的に削除を指定したり、削除する許可をしたりする ClassI のメンバ関数を作っても
構いませんが、キャッシュのように、状況に応じて自動的に判断することが処理効率が
良いこともあります。
Dim g_obj as ClassI
インターフェイスを
参照する変数
オブジェクトの
インターフェイス
オブジェクト(アトム)
get_Object 関数
オブジェクトを操作するインターフェイス(ClassI)は、演算器の種類(ClassV)と異なることが
あります。 さらに ClassV は、インスタンスのクラス(ClassA)と異なることもあります。
たとえば、ClassI =コンパイラ、ClassV = C言語コンパイラ、ClassA = A社製コンパイラver2。
状況や設定に応じて、適切な ClassA のインスタンスが返ります。 内部で、インスタンスが
生成されるかもしれないし、引数をそのまま維持するだけかもしれません。基本的に
インスタンスは1つ(シングレトン)ですが、マルチコアを生かしたり、マルチスレッドでレイテ
ンシを短くするために、Opt 引数に、新規インスタンスを指定することがあるかもしれません。
(VBScript は、基本的にシングルスレッドですが)
演算器的なオブジェクトのライフサイクル
参考
←
▼
▲
プログラムが使用する外部プログラムのバージョンが異なると、使用する
データが微妙に異なったりします。 たとえば、Visual Studio のプロジェクト・
ファイルを出力するプログラムは、Visual Studio のバージョンによって、
出力するファイルの拡張子が変わります。
プロジェクト・ファイルを出力する関数をプログラミングするときは、
バージョンを判定して、出力内容を変えるというのが一般的ですが、
規模が大きくなると、同じ条件分岐のコピーがたくさん記述されること
になり、メンテナンス性が低くなります。
たとえば、プロジェクト・ファイルの拡張子が変わると、ファイルを作成する
コードと、使用するプロジェクト・ファイルをソリューション・ファイル内に
記述するコードの2箇所に、バージョンを判定するコードが記述されます。
この状態で、将来、バージョンと拡張子の関係が変わったら、2箇所の修正が
必要なのに、1箇所だけ修正して完了としてしまうリスクがあるのです。
Dim o : Set o = new MakeVS
o.SetMakeProjContext MakeProjContext, get_MakeVSProjType( TypeName )
o.MakeProj
Class MakeVSProjType
Public VSVer '// as integer
Public TargetPlatform '// as string
Public ProjectExtension '// as string
End Class
Function get_MakeVSProjType( TypeName )
Dim ret, items
Select Case TypeName '// VSVer, TargetPlatform
Case "VS2010Win32" : items = Array( 2010, "Win32" )
Case "VS2008Win32" : items = Array( 2008, "Win32" )
Case "VS2010Win32Lib" : items = Array( 2010, "Lib" )
Case "VS2008Win32Lib" : items = Array( 2008, "Lib" )
Case Else Error
End Select
Set ret = new MakeVSProjType
ret.VSVer = items( 0 )
ret.TargetPlatform = items( 1 )
If ret.VSVer = 2010 Then
ret.ProjectExtension = ".vcxproj"
Else
ret.ProjectExtension = ".vcproj"
End If
End Function
これを避けるには、プロジェクト・ファイルの拡張子を変数にすることです。
この状態で、バージョンと拡張子の関係が変わったら、変数の値を代入する
箇所だけを変えるだけで済みます。
このような違いを集めたオブジェクトは、Visual Studio のバージョンによって
異なるものであるため、バージョンごとに存在する静的オブジェクト(コンパ
イル時に値が決定する static な構造体)になります。
そして、静的オブジェクトを取得する関数を定義するとよいでしょう。 その
定義は、下記のようにテーブルで記述するとよいでしょう。 また、相関関係が
ある場合は、下記の ProjectExtension 変数のように条件分岐が使えます。
この条件分岐は、静的オブジェクトを取得する関数の中にまとめられるため、
メンテナンス性が高くなります。
If VSVer <= 2008 Then f.Open ".vcproj" Else f.Open ".vcxproj"
f.Close
f.Open ".sln"
If VSVer <= 2008 Then f.Write ".vcproj" Else f.Write ".vcxproj"
←
▼
▲
'// Derived property
Private m_DerivedAttribute
Public Property Get DerivedAttribute()
If IsEmpty( m_DerivedAttribute ) Then m_DerivedAttribute = "Derived "+ Me.AnAttribute
DerivedAttribute = m_DerivedAttribute
End Property
read only attribute
DerivedAttribute
派生属性のキャッシュを持つときは、下記のようにコーディングします。
DerivedAttribute
DerivedAttribute
DerivedAttribute
DerivedAttribute
DerivedAttribute
m_ から始まるプライベート プロパティは、他の派生属性と並べると一覧性が高くてよいでしょう。
複数の派生属性を同時にキャッシュに載せいた方が、処理速度は速いでしょう。
←
▼
▲
参考
→ T_NameDelegator フォルダ
サンプルファイル
→ T_NameList フォルダ
→ T_Name フォルダ
Name と TrueName が、1対多の場合
Name と TrueName が、多対1の場合
Name と TrueName が、多対1の場合の一覧
Property Get Name() as string
→ vbslib を使う vbsool サンプル
インスタンスの名前です。 クラス名と同じになることもあります。
Property Get TrueName() as string
関数に指定する名前と同じです。
以下は、一般名 (Name) と正式名 (TrueName) の違いから生じる問題の対策法です。
→ Module Mixer 3 の実装
←
▼
▲
GetVar など)に正式名を指定することもできます。
クラス名やインスタンス名は、一般名と正式名があり、多くの場合、一般名を必要とします。しかし、
new に指定する型の名前や、TypeName で取得できる型の名前はクラスの正式名であることが
多く、扱いにくいでしょう。 そこで、
処理フロー
一般名は場面によって(抽象度が変化して)変わります。 正式名はインスタンスの内容や定義の
抽象度によって変わります。 一般名と正式名が、同じになることもあります。
一般名と正式名は、1対多の関係があるので、場面に応じて、一般名から正式名を選択する必要
があります。(同時に多対1の関係もありますが別の章を参照)。
( "Sample" )
new TrueSample
一般名 "Sample" のオブジェクトを取得する
'// Class Sample has_interface_of ClassI
Function get_Sample()
Set get_Sample = get_Object( Sample_getTrueName() )
get_Sample.Name = "Sample"
End Function
Function Sample_getTrueName()
Dim ret : ret = GetVar( "Sample" )
If IsEmpty( ret ) Then
'// default is ClassA fixed
ret = "TrueSample" : SetVar "Sample", ret
End If
Sample_getTrueName = ret
End Function
'// 言語上の Class Sample の定義は不要です。
一般名 "Sample" から正式名 "TrueSample" を選択
( "TrueSample" )
get_Object
上記は ret = "TrueSample" 固定ですが、場面に応じて TrueSample 以外の正式名を
返すように自動判定した方がよいです。
Name プロパティは、インスタンスの一般名とします。
TrueName プロパティは、インスタンスの正式名とします。
'// Class TrueSample has_interface_of ClassI
Dim g_TrueSample
Function get_TrueSample() '// has_interface_of ClassI
If IsEmpty( g_TrueSample ) Then _
Set g_TrueSample = new TrueSample
Set get_TrueSample = g_TrueSample
End Function
Class TrueSample
Public Name
Public Property Get TrueName() : TrueName = TypeName(Me) : End Property
'--- Name is factory pattern.
Private Sub Class_Initialize() : Name = TypeName(Me) : End Sub
:
End Class
ClassI
ClassB
ClassI
ClassA
Name = "ClassN", TrueName = "ClassA"
Name = "ClassN", TrueName = "ClassB"
データ構造
→ T_Name フォルダ
サンプルファイル
一般名で選択
例: Windows
例: Windows XP, Windows7
正式名 "TrueSample" のオブジェクトを取得する
[Sample]
[Sample]
[TrueSample]
[TrueSample]
→ T_Name_vbslib フォルダ
→ T_NameAdd_vbslib フォルダ
多くの場合、自動判定を行いますが、環境変数(レジストリや、設定ファイルや、vbslib の
上記の正式名を決める処理は、Sample_getTrueName 関数(一般名 "Sample" の
getTrueName メソッド)ではなく、get_Sample 関数に書かれていることがあります。
←
▼
▲
Dim g_NameDic : Set g_NameDic = CreateObject( "Scripting.Dictionary" )
Function get_NameDelegator( Name, TrueName, InterfaceName )
If g_NameDic.Exists( Name +"__"+ TrueName ) Then
Set get_NameDelegator = g_NameDic.Item( Name +"__"+ TrueName +"_"+ InterfaceName )
Exit Function
End If
Set get_NameDelegator = new_X( InterfaceName + "_Delegator" ) : With get_NameDelegator
.Name = Name
.m_Delegate = TrueName '// if validated was need.
If not g_bNeedValidateDelegate Then _
Set .m_Delegate = get_Object( TrueName ) '// if validated was not need.
End With
Set g_NameDic.Item( Name +"__"+ TrueName +"_"+ InterfaceName ) = get_NameDelegator
End Function
Class NameDelegator '// defined_as_interface
Public Name
Public Property Get TrueName() : TrueName = NameDelegator_getTrueName( Me ) : End Property
Public m_Delegate ' as ClassA or ClassB or string(before validated)
Public Property Get DefineInfo() : Set DefineInfo = m_Delegate.DefineInfo : End Property
End Class
複数から同じインスタンスを参照し、オブジェクトから一般名(Nameプロパティ)を取得する必要が
ある場合は、一般名と正式名が多対1の関係になり、次のような委譲をしないと、Name プロパティ
がそれぞれの場面に応じた一般名になりません。
'// Class Sample has_interface_of ClassI
Function get_Sample()
Set get_Sample = get_NameDelegator( "Sample", Sample_getTrueName(), "ClassI" )
End Function
Function Sample_getTrueName()
Dim ret : ret = GetVar( "Sample" )
If IsEmpty( ret ) Then
'// default is ClassA fixed
ret = "TrueSample" : SetVar "Sample", ret
End If
Sample_getTrueName = ret
End Function
'// Class ClassA has_interface_of ClassI
Dim g_TrueSample
Function get_TrueSample()
If IsEmpty( g_TrueSample ) Then _
Set g_TrueSample = new TrueSample
Set get_TrueSample = g_TrueSample
End Function
Class TrueSample
Public Property Get Name() : Name = TypeName(Me) : End Property
Public Property Get TrueName() : TrueName = TypeName(Me) : End Property
'--- Name is factory pattern.
Public Value
Public Function Method1() : Method1 = "TrueSample.Method1" : End Function
End Class
Name プロパティは、ユーザーが取り扱っている場面(ドメイン)によって抽象度が変化して、同じ
インスタンスに対して複数の一般名を持つことになります。 このケースに対応する実装は委譲の
形態をとるとよいでしょう。
他のドメインの名前を参照する必要は、ほとんどありませんが、もし参照するのであれば、Names
という集合のプロパティになります。
場面Aの関数
オブジェクトの削除は、ガーベジコレクションに任せて、ClassI_Delegator が無くなるまで存在させ
るとよいでしょう。
実体のファイルが削除されたショートカットのように、ClassI_Delegator が参照する、正式名の
オブジェクトだけを無くすときは、一般名から委譲先を参照している ClassI_Delegator::m_Delegate
を Empty にしてガーベジコレクションを動かして削除します。 また、Empty にすれば、削除された
オブジェクトにアクセスしようとしたときに、きちんと例外が発生するようになります。
Name 以外は
参照
場面Aでの Name
場面Bでの Name
ある1つのインスタンス
TrueName
(正式名)
ClassI_Delegator クラス
(一般名)
処理フロー
( "Sample" )
一般名 "Sample" のオブジェクトを取得する
一般名 "Sample" から、正式名 "TrueSample" を選択
new ClassI_Delegator
new TrueSample
If not g_NameDic.Exists( Name +"__"+ TrueName +"_"+ InterfaceName ) Then
InterfaceName インターフェイスを持ち、
TrueName へ委譲するオブジェクトを生成
正式名のオブジェクトを生成する
サンプルファイル
→ T_NameDelegator フォルダ
[Sample]
[Sample]
[TrueSample]
場面Bの関数
データ構造
g_NameDic
Dictionary
.Item
.Key
string
= Name +"__"+ TrueName +"_"+ InterfaceName
.Name
.m_Delegate
string
= "Sample"
ClassI
参考
new_X
( InterfaceName + "_Delegator" )
( Name, TrueName, InterfaceName )
ClassI
.Name = Name
Set .m_Delegate = get_Object( TrueName )
Public Property Get DefineInfo() : Set DefineInfo = m_Delegate.DefineInfo : End Property
DefineInfo プロパティは、オプションです。
Class ClassI_Delegator '// has_interface_of NameDelegator and ClassI
Public Name
Public Property Get TrueName() : TrueName = NameDelegator_getTrueName( Me ) : End Property
Public m_Delegate ' as ClassA or ClassB or string(before validated)
'--- Name is factory pattern.
Public Property Let Value( v ) : m_Delegate.Value = v : End Property
Public Property Get Value( ) : Value = m_Delegate.Value : End Property
Public Function Method1() : Method1 = m_Delegate.Method1() : End Function
End Class
Function new_ClassI_Delegator() '// has_interface_of NameDelegator and ClassI
Set new_ClassI_Delegator = new ClassI_Delegator
End Function
[ClassI_Delegator]
一般名を持ったオブジェクト
正式名を持ったオブジェクト
→ T_NameDelegator_vbslib フォルダ
例: Windows
例: Windows7
例: Windows7
←
▼
▲
オブジェクトの名前を一覧するときは、
取得して、Name プロパティを表示します。 TrueName は、必要なら補助的に表示します。
Sub ClassI::Validate( Opt as variant )
ただし、すべてのオブジェクトを生成すると時間がかかる場合、生成時に実行する初期化処理
(Class_Initialize) を Validate メソッドに移すとよいでしょう。
で一覧するすべてのオブジェクトを
Dim samples, sample
get_ObjectsFromFile "Lib\*_obj.vbs", "ClassI", samples '// [out] samples
For Each sample In samples : echo sample.Name : Next
一覧するときは、一般名が Name プロパティに入っているオブジェクトと、正式名が Name
プロパティに入っているオブジェクトの両方を一覧します。
→ T_NameList フォルダ
→ T_NameList_vbslib フォルダ
サンプルファイル
←
▼
▲
Default Property Item( Name as string ) as variant
VBScript は、ECMA Script (JavaScript) のように、動的にメンバーを定義することはできませんが、
デフォルト・プロパティと、辞書(Dictionary クラス)を使うことで、同様のことができます。
→ JavaScript のクラス
参考
Item プロパティは、Dictionary や FolderItem などに準拠しています。
ArrayClass(vbslib) も、Item プロパティが使えます。
→ T_Item フォルダ
サンプル:
Dim obj : Set obj = new ClassA
obj.PropA = 1 '// Static defined property
obj("PropB") = 12 '// Dynamic defined property
obj("PropC") = "ABC"
Set obj("PropD") = new ClassA
WScript.Echo "PropA = "& obj.PropA
WScript.Echo "PropB = "& obj("PropB")
WScript.Echo "PropC = "& obj("PropC").Value
使用例:
Class ClassA
Public PropA '// Static defined property
'//=== Dynamic defined property : Item, Items, Keys
Public Default Property Get Item( Name ) : LetSet Item, m_Dic( Name ) : End Property
Public Property Let Item( Name, Value ) : m_Dic( Name ) = Value : End Property
Public Property Set Item( Name, Object ) : Set m_Dic( Name ) = Object : End Property
Public Property Get Keys() : Keys = m_Dic.Keys : End Property
Public Property Get Items() : Items = m_Dic.Items : End Property
Public m_Dic ' as Dictionary
Private Sub Class_Initialize()
Set m_Dic = CreateObject( "Scripting.Dictionary" )
m_Dic.CompareMode = 1 '// NotCaseSensitive
End Sub
End Class
Sub LetSet( Out, In_ )
If IsObject( In_ ) Then Set Out = In_ Else Out = In_
End Sub
vbslib なし版
関連
→ T_Item_vbslib フォルダ
vbslib 使用版
→ GetSetting 関数
←
▼
▲
Property Get Items() as array of variant
For Each item In obj.Items
If IsObject( item ) Then
WScript.Echo item.Value
Else
WScript.Echo item
End If
Next
使用例:
サンプル
上記は標準形ですが、処理速度が要求されるときは次のようにしてください。
For Each item In obj.m_Dic.Items
←
▼
▲
Property Get Keys() as array of string
For Each name In obj.Keys
WScript.Echo name
Next
使用例:
サンプル
上記は標準形ですが、処理速度が要求されるときは次のようにしてください。
For Each item In obj.m_Dic.Keys
←
▼
▲
関連
関数定義のオーバーライド
Dim ClassE::OnEventX as EventResponders
イベントとは、あるオブジェクトの状態変化のことです。
イベントが発生したときに行う処理は、アプリケーションによって異なります。
そこで、あるイベントが発生したときに行う処理を関数に記述して、その関数をオブジェクトに
登録しておくことで、必要なときに必要な処理ができるようになります。
他の言語では、関数ポインタ、コールバック関数、メソッドポインタなどと呼ばれています。
Class ClassE
Public OnEventX ' as EventResponders
Private Sub Class_Initialize()
Set Me.OnEventX = new EventResponders
End Sub
End Class
Sub ClassA::OnEventX( Caller as ClassE, Args as variant )
Sub main()
Dim obj : Set obj = new ClassE '// defined by Library
Dim app : Set app = new ClassA '// defined by Application
app.RespondFromClassE obj
obj.Run '// call ClassA::OnEventX
End Sub
Class ClassA
Public Sub RespondFromClassE( Caller )
Caller.OnEventX.Add GetRef( "ClassA_onEventX" ), Me
End Sub
Public Sub OnEventX( Caller, Args )
WScipt.Echo "Responded"
End Sub
End Class
Sub ClassA_onEventX( Me_, Caller, Args ) : Me_.OnEventX( Caller, Args ) : End Sub
Class ClassE
Public OnEventX ' as EventResponders
Public Sub Run()
Me.OnEventX.Calls Me, Empty
End Sub
End Class
OnEventX
app
ClassA_onEventX
obj
Responded
OnEventX
OnEventX
複数のメソッドが登録されているときは、Calls メソッドを1度呼び出すだけで、複数の
メソッドが呼び出されます。
→ イベント関数とパラメータ取得関数 (C言語)
OnEventX
参考
関数を複数回呼び出す
→ イベント(C#)
イベントが発生したオブジェクトのメソッドから、イベントに応答するオブジェクトのメソッドを
呼び出すコードを、通常、ライブラリが書きます。
イベントが発生したらコールバックされるメソッドを、イベントが発生するオブジェクトに登録
するコードを、通常、アプリケーションが書きます。
通常、ライブラリにあるクラスが、イベントの定義とメソッドの呼び出しを行い、アプリケーション
が、イベントに応答するメソッドの定義と、そのメソッドの登録を行います。
通常、ライブラリにあるクラスが、イベントを定義します。
ライブラリの中 (イベント発生元)
アプリケーション
呼び出し
アプリケーションが定義する ClassA クラスが応答する場合:
アプリケーションが定義する OnEventX 関数が応答する場合:
Sub main()
Dim obj : Set obj = new ClassE
obj.OnEventX.Add GetRef( "OnEventX" ), Empty
obj.Run '// call OnEventX
End Sub
Sub OnEventX( Dummy, Caller, Args )
WScipt.Echo "Responded"
End Sub
Responded
OnEventX
OnEventX
OnEventX
OnEventX
→ T_Item フォルダ
サンプル:
vbslib なし版
→ T_Item_vbslib フォルダ
vbslib 使用版