サーチ…


抽象化

抽象レベルは、物事を分割する時期を決定するのに役立ちます。

抽象化は、ますます詳細なコードで機能を実装することによって達成されます。マクロのエントリーポイントは、 抽象度の高い小さなプロシージャーでなければならないので、何が起こっているのかを一目瞭然に把握できます

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値をカプセル化することで、チェックボックスの周りに抽象レイヤーを追加しました。


インタフェースを使用して不変性を強制する

専用のクラスモジュールにフォームのモデルを カプセル化することで、これをさらに進めてみましょう。しかし、 UserNameTimestamp 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インターフェイスからアクセスすると読み取り専用のプロパティ値を割り当てます。ここではTimestampUserName

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インターフェイスに対してコードを作成することができます。このインターフェイスでは、 TimestampUserName 、(コードがインターフェイスに対して記述されている限り)再割り当てできない読み取り専用プロパティとして公開されます。

多型

多態性とは、異なる基礎となる実装に同じインタフェースを提示する能力です。

インターフェイスを実装する機能により、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つ一度フォームを表示して手動でフォームコントロールをクリックしなくても、フォームのデータに対するユーザーのやりとりの順番を変えることができます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow