サーチ…
抽象化
抽象レベルは、物事を分割する時期を決定するのに役立ちます。
抽象化は、ますます詳細なコードで機能を実装することによって達成されます。マクロのエントリーポイントは、 抽象度の高い小さなプロシージャーでなければならないので、何が起こっているのかを一目瞭然に把握できます 。
Public Sub DoSomething()
With New SomeForm
Set .Model = CreateViewModel
.Show vbModal
If .IsCancelled Then Exit Sub
ProcessUserData .Model
End With
End Sub
DoSomething
プロシージャは抽象度が高いため、フォームを表示してモデルを作成し、そのオブジェクトを処理する方法を知っているProcessUserData
プロシージャにそのオブジェクトを渡すことができます。モデルの作成方法は別のプロシージャのジョブです。
Private Function CreateViewModel() As ISomeModel
Dim result As ISomeModel
Set result = SomeModel.Create(Now, Environ$("UserName"))
result.AvailableItems = GetAvailableItems
Set CreateViewModel = result
End Function
CreateViewModel
関数は、一部のISomeModel
インスタンスの作成のみを担当します。その責任の一部は、 利用可能なアイテムの配列を取得することです。これらのアイテムの取得方法は、 GetAvailableItems
プロシージャの背後で抽象化された実装の詳細です。
Private Function GetAvailableItems() As Variant
GetAvailableItems = DataSheet.Names("AvailableItems").RefersToRange
End Function
ここでは、 DataSheet
シートワークシートの名前付き範囲から使用可能な値を読み込みます。データベースから読み込むこともできますし、値をハードコードすることもできます。これは実装の詳細であり、高い抽象レベルのいずれにも関係しません。
カプセル化
カプセル化は、クライアントコードから実装の詳細を隠します。
Handler QueryCloseの例では、カプセル化の例を示します。フォームにはチェックボックスコントロールがありますが、そのクライアントコードは直接動作しません。チェックボックスは実装の詳細で、クライアントコードは設定が有効かどうかを知る必要があります。
チェックボックスの値が変更されると、ハンドラはプライベートフィールドメンバを割り当てます。
Private Type TView IsCancelled As Boolean SomeOtherSetting As Boolean 'other properties skipped for brievety End Type Private this As TView '... Private Sub SomeOtherSettingInput_Change() this.SomeOtherSetting = CBool(SomeOtherSettingInput.Value) End Sub
クライアントコードがその値を読みたい場合、チェックボックスを心配する必要はありません。代わりに単にSomeOtherSetting
プロパティを使用します:
Public Property Get SomeOtherSetting() As Boolean SomeOtherSetting = this.SomeOtherSetting End Property
SomeOtherSetting
プロパティは 、チェックボックスの状態をカプセル化します。クライアントコードにはチェックボックスが含まれていることを知る必要はなく、ブール値の設定しかありません。 Boolean
値をカプセル化することで、チェックボックスの周りに抽象レイヤーを追加しました。
インタフェースを使用して不変性を強制する
専用のクラスモジュールにフォームのモデルを カプセル化することで、これをさらに進めてみましょう。しかし、 UserName
とTimestamp
Public Property
を作成した場合は、 Property Let
アクセサを公開してプロパティを変更可能にする必要があります。クライアントコードでは、設定後にこれらの値を変更する必要はありません。
抽象化の例のCreateViewModel
関数は、 ISomeModel
クラスを返します。これは私たちのインターフェイスであり、次のようになります。
Option Explicit
Public Property Get Timestamp() As Date
End Property
Public Property Get UserName() As String
End Property
Public Property Get AvailableItems() As Variant
End Property
Public Property Let AvailableItems(ByRef value As Variant)
End Property
Public Property Get SomeSetting() As String
End Property
Public Property Let SomeSetting(ByVal value As String)
End Property
Public Property Get SomeOtherSetting() As Boolean
End Property
Public Property Let SomeOtherSetting(ByVal value As Boolean)
End Property
Notice Timestamp
プロパティとUserName
プロパティは、 Property Get
アクセサのみを公開します。今度はSomeModel
クラスがそのインタフェースを実装できます:
Option Explicit
Implements ISomeModel
Private Type TModel
Timestamp As Date
UserName As String
SomeSetting As String
SomeOtherSetting As Boolean
AvailableItems As Variant
End Type
Private this As TModel
Private Property Get ISomeModel_Timestamp() As Date
ISomeModel_Timestamp = this.Timestamp
End Property
Private Property Get ISomeModel_UserName() As String
ISomeModel_UserName = this.UserName
End Property
Private Property Get ISomeModel_AvailableItems() As Variant
ISomeModel_AvailableItems = this.AvailableItems
End Property
Private Property Let ISomeModel_AvailableItems(ByRef value As Variant)
this.AvailableItems = value
End Property
Private Property Get ISomeModel_SomeSetting() As String
ISomeModel_SomeSetting = this.SomeSetting
End Property
Private Property Let ISomeModel_SomeSetting(ByVal value As String)
this.SomeSetting = value
End Property
Private Property Get ISomeModel_SomeOtherSetting() As Boolean
ISomeModel_SomeOtherSetting = this.SomeOtherSetting
End Property
Private Property Let ISomeModel_SomeOtherSetting(ByVal value As Boolean)
this.SomeOtherSetting = value
End Property
Public Property Get Timestamp() As Date
Timestamp = this.Timestamp
End Property
Public Property Let Timestamp(ByVal value As Date)
this.Timestamp = value
End Property
Public Property Get UserName() As String
UserName = this.UserName
End Property
Public Property Let UserName(ByVal value As String)
this.UserName = value
End Property
Public Property Get AvailableItems() As Variant
AvailableItems = this.AvailableItems
End Property
Public Property Let AvailableItems(ByRef value As Variant)
this.AvailableItems = value
End Property
Public Property Get SomeSetting() As String
SomeSetting = this.SomeSetting
End Property
Public Property Let SomeSetting(ByVal value As String)
this.SomeSetting = value
End Property
Public Property Get SomeOtherSetting() As Boolean
SomeOtherSetting = this.SomeOtherSetting
End Property
Public Property Let SomeOtherSetting(ByVal value As Boolean)
this.SomeOtherSetting = value
End Property
インタフェースメンバはすべてPrivate
であり、コードをコンパイルするためにはインタフェースのすべてのメンバを実装する必要があります。 Public
メンバーはインターフェイスの一部ではないため、 ISomeModel
インターフェイスに対して記述されたコードに公開されません。
ファクトリメソッドを使用してコンストラクタをシミュレートする
VB_PredeclaredId属性を使用すると、 SomeModel
クラスに既定のインスタンスが作成static
れ、クライアントコードが最初に作成する必要なく呼び出すことができるタイプレベル(VB.NETでShared
、C#でstatic
)メンバーのように機能する関数を記述できます私たちがここで行ったように、インスタンス:
Private Function CreateViewModel() As ISomeModel Dim result As ISomeModel Set result = SomeModel.Create(Now, Environ$("UserName")) result.AvailableItems = GetAvailableItems Set CreateViewModel = result End Function
このファクトリメソッドは、 ISomeModel
インターフェイスからアクセスすると読み取り専用のプロパティ値を割り当てます。ここではTimestamp
とUserName
。
Public Function Create(ByVal pTimeStamp As Date, ByVal pUserName As String) As ISomeModel
With New SomeModel
.Timestamp = pTimeStamp
.UserName = pUserName
Set Create = .Self
End With
End Function
Public Property Get Self() As ISomeModel
Set Self = Me
End Property
そして、 ISomeModel
インターフェイスに対してコードを作成することができます。このインターフェイスでは、 Timestamp
とUserName
、(コードがインターフェイスに対して記述されている限り)再割り当てできない読み取り専用プロパティとして公開されます。
多型
多態性とは、異なる基礎となる実装に同じインタフェースを提示する能力です。
インターフェイスを実装する機能により、UI、データベース、またはこのワークシートからアプリケーションロジックを完全に切り離すことができます。
フォーム自体に実装されているISomeView
インターフェイスがあるとします。
Option Explicit
Public Property Get IsCancelled() As Boolean
End Property
Public Property Get Model() As ISomeModel
End Property
Public Property Set Model(ByVal value As ISomeModel)
End Property
Public Sub Show()
End Sub
フォームのコードビハインドは次のようになります。
Option Explicit
Implements ISomeView
Private Type TView
IsCancelled As Boolean
Model As ISomeModel
End Type
Private this As TView
Private Property Get ISomeView_IsCancelled() As Boolean
ISomeView_IsCancelled = this.IsCancelled
End Property
Private Property Get ISomeView_Model() As ISomeModel
Set ISomeView_Model = this.Model
End Property
Private Property Set ISomeView_Model(ByVal value As ISomeModel)
Set this.Model = value
End Property
Private Sub ISomeView_Show()
Me.Show vbModal
End Sub
Private Sub SomeOtherSettingInput_Change()
this.Model.SomeOtherSetting = CBool(SomeOtherSettingInput.Value)
End Sub
'...other event handlers...
Private Sub OkButton_Click()
Me.Hide
End Sub
Private Sub CancelButton_Click()
this.IsCancelled = True
Me.Hide
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
this.IsCancelled = True
Me.Hide
End If
End Sub
しかし、ユーザーフォームなしで ISomeView
インターフェイスを実装する別のクラスモジュールを作成することは禁止されていません。これはSomeViewMock
クラスになります。
Option Explicit
Implements ISomeView
Private Type TView
IsCancelled As Boolean
Model As ISomeModel
End Type
Private this As TView
Public Property Get IsCancelled() As Boolean
IsCancelled = this.IsCancelled
End Property
Public Property Let IsCancelled(ByVal value As Boolean)
this.IsCancelled = value
End Property
Private Property Get ISomeView_IsCancelled() As Boolean
ISomeView_IsCancelled = this.IsCancelled
End Property
Private Property Get ISomeView_Model() As ISomeModel
Set ISomeView_Model = this.Model
End Property
Private Property Set ISomeView_Model(ByVal value As ISomeModel)
Set this.Model = value
End Property
Private Sub ISomeView_Show()
'do nothing
End Sub
そして今、 UserForm
で動作するコードを変更して、インスタンス化するのではなくパラメータとしてフォームを渡すなどして、 ISomeView
インターフェイスを動作させることができます:
Public Sub DoSomething(ByVal view As ISomeView)
With view
Set .Model = CreateViewModel
.Show
If .IsCancelled Then Exit Sub
ProcessUserData .Model
End With
End Sub
のでDoSomething
方法は、(すなわち、抽象化 )およびない具象クラス (例えば、特定のインターフェイスに依存UserForm
)、我々はことを保証し、自動ユニットテスト書くことができProcessUserData
したときに実行されていないview.IsCancelled
あるTrue
私たちを作ることによって、テストは、作成SomeViewMock
その設定、インスタンスをIsCancelled
にプロパティをTrue
、そしてそれを渡すDoSomething
。
テスト可能なコードは抽象に依存する
VBAで単体テストを書くことができます。そこにはIDEにも統合するアドインがあります。しかし、コードがワークシート、データベース、フォーム、またはファイルシステムと緊密に結合されている場合、単体テストは実際のワークシート、データベース、フォーム、またはファイルシステムを必要とし始めます。これらの依存関係は新しい制御不能障害単体テストは実際のワークシート、データベース、フォーム、またはファイルシステムを必要としないように、テスト可能なコードを分離する必要があります。
テストコードがスタブ/模擬実装(上記のSomeViewMock
例のように)を注入できるように、インターフェイスに対してコードを書くことによって、 "制御された環境"でテストを記述し、可能な限り1つ1つ一度フォームを表示して手動でフォームコントロールをクリックしなくても、フォームのデータに対するユーザーのやりとりの順番を変えることができます。