수색…
추출
추상화 레벨은 상황을 분할 할시기를 결정하는 데 도움이됩니다.
추상화는 점차 세부적인 코드로 기능을 구현함으로써 성취됩니다. 매크로의 시작점은 높은 추상화 수준 의 작은 프로 시저로, 상황을 한 눈에 파악하기 쉽습니다.
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
시트 워크 시트의 명명 된 범위에서 사용 가능한 값을 읽는 것입니다. 데이터베이스에서 값을 읽는 것만으로도 값을 하드 코딩 할 수 있습니다. 더 높은 추상화 수준에 대해서는 아무런 관심도없는 구현 세부 사항 입니다.
캡슐화
캡슐화는 클라이언트 코드에서 구현 세부 사항을 숨 깁니다.
Handling 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
때 체크 박스에 대해 걱정할 필요가 없습니다. 대신 단순히 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
알림 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
클래스에 기본 인스턴스 가 있고, 클라이언트 코드가 먼저 만들 필요없이 호출 할 수있는 유형 수준 (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
을 코드 재 지정이 ISomeModel
읽기 전용 속성으로 노출합니다 (코드가 인터페이스에 대해 작성된 경우).
다형성
다형성은 서로 다른 기본 구현에 대해 동일한 인터페이스를 제공 할 수있는 기능입니다.
인터페이스를 구현하는 기능을 통해 응용 프로그램 논리를 UI, 데이터베이스 또는이 워크 시트에서 완전히 분리 할 수 있습니다.
양식 자체에서 구현하는 ISomeView
인터페이스가 있다고 가정 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
인터페이스를 구현하는 또 다른 클래스 모듈을 만드는 것을 금지 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
)가 아닌 인터페이스 (즉, 추상화 )에 의존하기 때문에 view.IsCancelled
가 True
일 때 ProcessUserData
가 실행되지 않도록하는 자동 단위 테스트를 작성할 수 있습니다. 테스트를 통해 SomeViewMock
인스턴스를 만들고 IsCancelled
속성을 True
설정 한 IsCancelled
DoSomething
전달합니다.
테스트 가능한 코드는 추상화에 따라 다릅니다.
VBA에서 단위 테스트를 작성하면 IDE에 통합하는 추가 기능이 있습니다. 그러나 코드가 워크 시트, 데이터베이스, 폼 또는 파일 시스템과 밀접하게 결합 되면 단위 테스트는 실제 워크 시트, 데이터베이스, 폼 또는 파일 시스템을 필요로하기 시작합니다. 이러한 종속성 은 새로운 제어 불능 실패입니다 단위 테스트에서 실제 워크 시트, 데이터베이스, 폼 또는 파일 시스템을 필요로 하지 않도록 테스트 가능한 코드를 분리해야합니다.
테스트 코드가 스텁 / 모의 구현물 (예 : 위의 SomeViewMock
예제)을 삽입 할 수 있도록 인터페이스에 대해 코드를 작성하면 "제어 된 환경"에서 테스트를 작성하고 가능한 모든 가능한 경우에 어떤 일이 발생하는지 시뮬레이션 할 수 있습니다 양식을 표시하고 양식 컨트롤을 수동으로 클릭하지 않고도 양식의 데이터에 대한 사용자 상호 작용의 순열